2010-03-15 2 views
1

시도하고있는 것이 가능한지 확실하지 않지만 개체 부모 속성에서 linq 표현식을 다시 사용하고 싶습니다. 주어진 클래스와Linq - 하위 속성에 표현식을 다시 사용하십시오.

:

:

class Parent { 
int Id { get; set; } 
IList<Child> Children { get; set; } 
string Name { get; set; } 
} 
class Child{ 
int Id { get; set; } 
Parent Dad { get; set; } 
string Name { get; set; } 
} 

난 다음의 라인을 따라 도우미

Expression<Func<Parent,bool> ParentQuery() { 
    Expression<Func<Parent,bool> q = p => p.Name=="foo"; 
} 
그때 아이를 위해 밖으로 데이터를 쿼리 할 때이를 사용할

가있는 경우

using(var context=new Entities.Context) { 
var data=context.Child.Where(c => c.Name=="bar" 
&& c.Dad.Where(ParentQuery)); 
} 

자녀 컬렉션에서 할 수 있음을 알고 있습니다.

using(var context=new Entities.Context) { 
var data=context.Parent.Where(p => p.Name=="foo" 
&& p.Childen.Where(childQuery)); 
} 

하지만 컬렉션이 아닌 속성에서이 작업을 수행 할 수는 없습니다.
이것은 단순한 예일뿐입니다. 실제로 ParentQuery가 더 복잡해지기 때문에이 위치를 여러 곳에서 반복하지 않도록하고 싶습니다. 5 개 또는 6 개의 레이어를 추가하는 것보다는 2 개의 레이어를 추가해야하지만 모두 상위 쿼리를 참조하여 보안을 보장합니다.

이것이 가능하지 않다면 다른 생각은 ParentQuery 표현식을 주어진 유형으로 변환하는 것이 었습니다. p => p.Name == "foo"; 은 다음으로 바뀝니다. c => c.Dad.Name == "foo"; 하지만 generics/다른 형태의 쿼리 작성기를 사용하여 상위 쿼리를 유지 한 다음 속성 경로를 상위로 대체하는 하위 개체 당 변환기를 작성해야합니다.

편집 : 난 그냥 대리자 함수로 표현에서 변경 한 다음 어디에요 (ParentQuery() (C를 호출 할 수 있습니다처럼 는 @ 데이비드 모튼

에 의해 코멘트에 이어 처음에 그 보인다. 아빠));

그러나 더 넓은 저장소 패턴에서이를 사용하고 있으며 generics 및 predicate 빌더에서 어떻게 사용할 수 있는지 보지 못했습니다. 클라이언트의 저장소 (이 경우 웹 서버)에서 행을 검색하고 싶지 않습니다. 기본 식 쿼리를 사용하는 일반 get 데이터 메서드가 있습니다. 그런 다음 제공된 유형이 ISecuredEntity를 구현하는지, 그리고 처리중인 엔티티에 대해 securityQuery를 추가하는지 테스트하려고합니다.

public static IList<T> GetData<T >(Expression<Func<T, bool>> query) { 
IList<T> data=null; 
var secQuery=RepositoryHelperers.GetScurityQuery<T>(); 
if(secQuery!=null) { 
    query.And(secQuery); 
} 
using(var context=new Entities.Context()) { 
    var d=context.GetGenericEntitySet<T>(); 
    data=d.ToList(); 
} 
return data; 
} 

ISecuredEntity :

public interface ISecuredEntity : IEntityBase { 
    Expression<Func<T, bool>> SecurityQuery<T>(); 
} 

엔티티 예 :

public partial class ExampleEntity: ISecuredEntity { 
    public Expression<Func<T, bool>> SecurityQuery<T>() { 
     //get specific type expression and make generic 
     Type genType = typeof(Func<,>).MakeGenericType(typeof(ExampleEntity), typeof(bool)); 
     var q = this.SecurityQuery(user); 
     return (Expression<Func<T, bool>>)Expression.Lambda(genType, q.Body, q.Parameters);   
    } 

    public Expression<Func<ExampleEntity, bool>> SecurityQuery() { 
     return e => e.OwnerId==currentUser.Id; 

    } 
} 

및 repositoryHelpers : 여기

internal static partial class RepositoryHelpers { 
    internal static Expression<Func<T, bool>> SecureQuery<T>() where T : new() { 
     var instanceOfT = new T(); 
     if (typeof(Entities.ISecuredEntity).IsAssignableFrom(typeof(T))) { 
      return ((Entities.ISecuredEntity)instanceOfT).SecurityQuery<T>(); 
     } 
     return null; 
    } 
} 

EDIT는 (최종) 인 솔루션

위로 가기 식으로 돌아가서 LinqKit Invoke를 사용했다. 참고 : EF의 경우에도 전화해야했습니다.entitySet

의 핵심 부분에 AsExpandable은()를 호출 할 수있는된다

Product.SecureFunction(user).Invoke(pd.ParentProduct); 

을 내가 맥락에서 전달할 수 있다는 부모님의 질의에

내 최종 클래스는 다음과 같다 :

public interface ISecureEntity { 
Func<T,bool> SecureFunction<T>(UserAccount user); 
} 


public class Product : ISecureEntity { 
public Expression<Func<T,bool>> SecureFunction<T>(UserAccount user) { 
    return SecureFunction(user) as Expression<Func<T,bool>>; 
} 
public static Expression<Func<Product,bool>> SecureFunction(UserAccount user) { 
    return f => f.OwnerId==user.AccountId; 
} 
public string Name { get;set; } 
public string OwnerId { get;set; } 
} 


public class ProductDetail : ISecureEntity { 
public Expression<Func<T,bool>> SecureFunction<T>(UserAccount user) { 
    return SecureFunction(user) as Expression<Func<T,bool>>; 
} 
public static Func<ProductDetail,bool> SecureFunction(UserAccount user) { 
    return pd => Product.SecureFunction(user).Invoke(pd.ParentProduct); 
} 
public int DetailId { get;set; } 
public string DetailText { get;set; } 
public Product ParentProduct { get;set; } 
} 

사용법 :

public IList<T> GetData<T>() { 
IList<T> data=null; 
Expression<Func<T,bool>> query=GetSecurityQuery<T>(); 
using(var context=new Context()) { 
    var d=context.GetGenericEntitySet<T>().Where(query); 
    data=d.ToList(); 
} 
return data; 
} 
private Expression<Func<T,bool>> GetSecurityQuery<T>() where T : new() { 
    var instanceOfT = new T(); 
     if (typeof(Entities.ISecuredEntity).IsAssignableFrom(typeof(T))) { 
      return ((Entities.ISecuredEntity)instanceOfT).SecurityQuery<T>(GetCurrentUser()); 
     } 
     return a => true; //returning a dummy query 
    } 
} 

도움을 주셔서 감사합니다.

답변

1

지나치게 생각하고 있습니다.

먼저 Expression<Func<Parent, bool>>을 반환하지 마십시오. 표현식을 컴파일해야합니다. 대신 간단히 Func<Parent, bool>을 반환하십시오. 큰 먹으 렴, 거의 내가 원하는 것을 -

context.Children.Where(c => c.Name == "bar" && ParentQuery()(c.Dad)); 

context.Parents.Where(ParentQuery()); 
+0

아 :

다음, 당신이 그것을 호출하는 방법에 전부입니다. ParentQuery 내에서 Epression을 구축하기 위해 LinqKut을 사용 했으므로 Expression steve

+0

좋아, 거의 다 .. 내 구현에서 이 ParentQuery 실제로 공공 Func을 SecurityQuery ()과 같은 인터페이스에 선언 이전에 그때 Expression.Lambda를 통해 일반적인 표현을 구축하여 강력한 형식의 쿼리를 반환(). 공용 Func SecurityQuery()를 어떻게 Func 으로 변환 할 수 있습니까? 내가 누락 된 쉬운 방법이 있습니까? – steve

+0

더 많은 테스트를 해본 결과 이것이 나의 마지막 장애물입니다 - 어떻게 Func 을 Func 으로 변환 할 수 있습니까? (런타임에는 T가 부모가됩니다) – steve

관련 문제