저는 프로젝트 데이터 저장소에 객체 매핑을 캡슐화하려고했습니다. 어쩌면 EF는 필요한 추상화 수준을 제공 할 것이지만 지금 Linq를 SQL로 사용하고있는 여러 가지 이유에서 볼 수 있습니다. 나는 이후가 MapResource 식을 호출 할 수있는 코드는 실패합니다Linq를 SQL 데이터 액세스에 캡슐화하는 가장 좋은 방법은 무엇입니까?
public List<ModUser> GetUsers() {
Users.Select(MapUser).ToList();
}
public Expression<Func<User, ModUser>> MapUser {
get {
return u => new ModUser() {
UserId = u.User_Id,
UserResources = u.Resources(MapResource)
}
}
}
public Expression<Func<Resource, ModResource>> MapResource { ...
: 다음 코드는 ModUser 저장소가 노출하는 POCO입니다 ModUser 개체의 목록으로 데이터베이스에 사용자를 반환하는 것을 목표로 다른 식에서 그것을 호출하려고합니다. 나는 이것을 "MapResource"를 u => 새로운 ModResource()로 대체 한 다음 ExpressionVisitor를 사용하여이 자리 표시 자 노드를 찾아 MapResource 표현식으로 대체함으로써이 문제를 해결했습니다.
ModUser의 속성에 단일 속성, 즉 UserResource = MapResource와 관련된 표현식을 지정하려고하면 비슷한 문제가 발생합니다. Expression 클래스의 메서드를 사용하여 필요한 식을 수동으로 결합하여이 두 번째 문제를 해결할 수있었습니다.
나는 위의 코드를 변경할 수 있음을 깨닫는다 다음
UserResources = u.Resources(r => MapResource.Compile().Invoke(r));
그러나에 우리가 있기 때문에, MapResouce에 필요한 모든 R의 속성이 아니라 사람을 얻을 필요가 생성되는 최종 SQL 쿼리 지금은 함수를 다루고 있습니다. 또한 MapResouce가 표현식이 아닌 함수로 사용되기 때문에 더 이상 테이블에 액세스 할 수 없으면 불가능합니다. DeferredLoadingEnabled를 true로 설정할 수 있지만 필요한 모든 테이블과 조인하도록 기본 쿼리를 수정하는 대신 개별 쿼리가 여러 개 생성됩니다.
이 작업이 .NET의 이후 버전에서 더 쉽게 될 것인지 아니면 내가 잘못된 방향으로 가고 있는지 아는 사람이 있습니까? Linq 및 Expression 기능이 정말 마음에 듭니다. 더 읽기 쉬운 코드를 사용하여 구현할 수 있기를 바랍니다.
업데이트
생각 나는 표현이 더 작성 가능 만든 방법에 대한 몇 가지 사례를 추가 할 수 있습니다. 그들은 간결하지는 않지만 일을 끝내게됩니다.
public Expression<Func<User, ModUser>> MapUser {
get {
Expression<Func<User, ModUser>> mapUser = u => new ModUser() {
UserId = u.User_Id,
UserResources = u.Resources(r => new ModResource())
};
return mapUser.MapResources(this);
}
}
public Expression<Func<Resource, ModResource>> MapResource { ... }
public static Expression<Func<T0, T1>> MapResources<T0, T1>(this Expression<Func<T0, T1>> exp, DataContext dc) {
return exp.Visit<MethodCallExpression, Expression<Func<T0, T1>>>(m => {
if(m.Arguments.Count > 1 && m.Arguments[1].Type == typeof(Func<DataContext.Resource, ModResource>)) { //Find a select statement that has the sub expression as an argument
//The resource mapping expression will require the Resource object, which is obtained here
ParameterExpression resourceParam = ((LambdaExpression)m.Arguments[1]).Parameters[0];
return Expression.Call(m.Method, m.Arguments[0], //The first argument is the record selection for the 'select' method
Expression.Lambda<Func<DataContext.Resource, ModResource>>(//Provide the proper mapping expression as the projection for the 'select' method
Expression.Invoke(dc.MapResource, resourceParam),
resourceParam)
);
}
return m;
});
}
여기 내가 뭘하고 있니? 이 버전의 MapUser에서는 ModResource 객체를 올바르게 만들지 않으며, 그냥 더미 버전을 만듭니다. 그런 다음 모호한 호출을 찾아 원래의 호출로 대체하는 표현식 방문자 메서드를 호출합니다. 나에게 원래 표현식 트리를 기본적으로 구성 할 수 있으므로 표현 구문이 부족한 것 같지만 트리를 실제로 실행해야한다. 아래는 단일 사건을 다루는 내가 찾은 또 다른 해결 방법입니다 :
public Expression<Func<User, ModUser>> MapUser {
get {
Expression<Func<User, ModResource, ModUser>> mapUser = (u, resource) => new ModUser() {
UserId = u.User_Id,
UserResource = resource;
}
return mapUser.CollapseArgument(MapResource, user => user.MainResource);
}
}
public Expression<Func<Resource, ModResource>> MapResource { ... }
public static Expression<Func<T0, T3>> CollapseArgument<T0, T1, T2, T3>(this Expression<Func<T0, T1, T3>> exp, Expression<Func<T2, T1>> exp0, Expression<Func<T0, T2>> exp1) {
var param0 = Expression.Parameter(typeof(T0), "p0");
var argExp = Expression.Invoke(exp0, Expression.Invoke(exp1, param0));
return Expression.Lambda<Func<T0, T3>>(
Expression.Invoke(exp, param0, argExp),
param0);
}
내가 사용자 데이터에서 자원 데이터를 얻을 수 있다는 사실을 알고이 두 번째 예에서하지만 난 수 없습니다 "인라인"로 표현 이를 수행하고 리소스 데이터를 리소스 POCO에 매핑하는 방법을 보여줍니다. 하지만 이미 매핑 된 리소스 POCO가 주어진 식 트리를 수동으로 생성하여 사용할 수 있습니다. 그런 다음 사용자로부터 자원 원시 데이터를 얻는 방법과 원시 자원 데이터를 자원 POCO로 맵핑하는 방법을 보여주는 최종 표현식을 표시하는 다른 표현식을 작성할 수 있습니다. 이제는이 모든 정보를 하나의 표현 트리로 결합하여 자원 별 매개 변수를 "축소"하는 방식으로 생각할 수 있습니다. 기본 사용자 매개 변수에서 얻을 수 있기 때문입니다. 이것은 위의 코드가하는 것입니다.
그래서 표현식을 매우 구성 가능하게 만드는 방법을 찾았습니다 ...그것은 단지 깨끗하다고 느끼지 않습니다.
@ 크리스가 사용하지 않은 자 NHibernate,하지만 "코드가"xml 파일에 매핑 움직이지 않는다? – eglasius
Fluent NHibernate를 사용하십시오 - 그러면 XML 대신에 모든 리팩토링/컴파일 시간 검증을 통해 코드를 얻을 수 있습니다. –
NHibernate로 이동하는 것이이 프로젝트의 옵션이 아니기 때문에 나는 Linq to SQL 솔루션을 찾고있다. – LaserJesus