2010-03-15 38 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,但是他們都需要引用父查詢以確保安全性。

如果這不可能,我的另一個想法是以某種方式將ParentQuery表達式有效地轉換爲給定類型: p => p.Name ==「foo」;變成: c => c.Dad.Name ==「foo」; ,但使用泛型/一些其他形式的查詢生成器,它允許這保留父查詢,然後只需構建一個翻譯器每個子對象替代在屬性路由到父。

編輯: 通過@大衛·莫頓

從評論繼最初看起來像我剛剛從表達式更改爲委託函數,然後調用 。凡(ParentQuery()(C。爸));

但是我在更廣泛的存儲庫模式中使用它,並且不知道如何將它用於泛型和謂詞構建器 - 我不想從存儲中檢索行並在客戶端(本例中爲web服務器)上進行過濾。我有一個通用的get數據方法,它接受基礎表達式查詢。然後我想測試一下,看看提供的類型是否實現了ISecuredEntity,以及它是否爲我們正在處理的實體附加了安全查詢。

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我也必須打電話。AsExpandable()在EntitySet的

的關鍵部分是能夠撥打:

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

啊 - 那是偉大的,幾乎我想要做什麼。 我正在使用LinqKut在ParentQuery中構建epression,因此Expression steve

+0

好,幾乎有.. 我在執行,ParentQuery實際上是宣佈在接口上爲公衆Func鍵 SecurityQuery () 我以前然後通過Expression.Lambda建立一個通用的表達式返回一個強類型的查詢()。 如果我有公共Func SecurityQuery()如何將其轉換爲Func ? 當然,有一種簡單的方法,我失蹤了? – steve

+0

已經做了一些更多的測試,這是我的最後一道關口 - 如何將fet Func 轉換爲Func (在運行時,T將是父項) – steve