2016-07-14 54 views
3

我有一個很多DbSets的DbContext。每個DbSet都應該有一個函數,可以從set中獲取一個頁面,並使用給定的pageSize並按特定的sortOrder排序。喜歡的東西:如何創建表達式<Func <TSource,bool>通過比較Func <TSource,int>與int

var pageItems = dbContext.Posts 
    .Where(post => post.BlogId == blogId) 
    .OrderBy(some sortorder) 
    .Skip(pageNr * pageSize) 
    .Take(pageSize); 

我希望能夠用我所有的DbSets要做到這一點,所以我創造了這個外鍵應有的擴展方法,其中的一個參數指定的外鍵比較和另一個值。

public static IQueryable<TSource> GetPage<TSource>(this IQueryable<TSource> source, 
    int pageNr, int pageSize, 
    Expression<Func<TSource, Tproperty>> keySelector, Tproperty comparisonValue) 
{ 
    return source 
    .Where(???) 
    .OrderBy(some sortorder) 
    .Skip(pageNr * pageSize) 
    .Take(pageSize); 
} 

如何將keySelector轉換爲適合Where的謂詞?

+0

Jon Skeet到達 –

+0

您可以嘗試使用'Expression.LessThan'並將您的'keySelector'和'comprasionValue'的body傳遞給'Expression.Constant'。 –

+0

讓我們來解釋Where。你將如何解決'OrderBy'? –

回答

1

你正在尋找一種方式來獲得從Expression<Func<TSource, Tproperty>> keySelectorExpression<Func<TSource, boolean>>並以這樣的方式Tproperty comparisonValue,它可以被翻譯成由實體框架存儲表達式。

這意味着瑣碎

public static Expression<Func<TSource, bool>> KeyPredicateNaive<TSource, Tproperty>(Expression<Func<TSource, Tproperty>> keySelector, Tproperty comparisonValue) 
{ 
    return (TSource source) =>EqualityComparer<Tproperty>.Default.Equals(keySelector.Compile()(source), comparisonValue); 
} 

將無法​​正常工作。這不能轉換爲商店表達式。

我們需要手動構建表達式。我們需要的是一個關鍵選擇器作爲其左值的等式表達式,以及一個常量表達式,其中比較值作爲值作爲其右值。我們可以按如下方式構建:

public static Expression<Func<TSource, bool>> KeyPredicate<TSource, Tproperty>(Expression<Func<TSource, Tproperty>> keySelector, Tproperty comparisonValue) 
{ 
    var bd = Expression.Equal(keySelector.Body, Expression.Constant(comparisonValue)); 
    return Expression.Lambda<Func<TSource, bool>>(bd, keySelector.Parameters); 
} 

可以將結果傳遞給您的where類。瘦身(因此它會編譯和運行),你的方法看起來就像

public static IQueryable<TSource> GetPage<TSource>(this IQueryable<TSource> source, 
    int pageSize, 
    Expression<Func<TSource, Tproperty>> keySelector, Tproperty comparisonValue) 
{ 
    return source 
    .Where(KeyPredicate(keySelector, comparisonValue) 
    .Take(pageSize); 
} 

我會用這個?可能不會。將謂詞直接作爲lambda函數傳遞給函數會更容易,而不是自己構造表達式。但這當然是一種可能性。

+0

此方法有效。我已經發現了一些關於像你一樣創建表達的文章。但是我找不到如何從BinaryExpresson創建lambda表達式。你是對的,傳遞Func 而不是傳遞Func 更容易,它給出了相同級別的檢查。 –

+0

儘管如此,在你的工具帶中使用這種技術是很好的,所以下次如果你正在考慮類似的東西,那麼判斷這是不是一個好主意會更容易。 – Martijn

+0

與此答案相同,提前2個小時http://stackoverflow.com/a/38372514/360211 – weston

0

你正在寫擴展到可查詢源,是嗎?因此,只要通過表情和過濾器來源:

public static IQueryable<TSource> GetPage<TSource, TKey>(this IQueryable<TSource> source, 
    Expression<Func<TSource, bool>> predicate, 
    Expression<Func<TSource, TKey>> keySelector, 
    int pageNr, int pageSize 
    ) 
{ 
    return source 
     .Where(predicate) 
     .OrderBy(keySelector) 
     .Skip(pageNr * pageSize) 
     .Take(pageSize); 
} 

用法:

db.Posts.GetPage(p => p.Author == "Bob", p => p.Date, 5, 10); 

注意:在你的方法,你有問題,分選(第二表達式),你得到的是傳遞兩個參數p => p.Author, "Bob"代替通過一個現成的表達p => p.Author == "Bob"


但我會移動predicate和keySelector出GetPage方法。 讓這個方法專注於尋呼只(如方法名狀態):

public static IQueryable<TSource> GetPage<TSource, TKey>(this IQueryable<TSource> source, 
    int pageNr, int pageSize) 
{ 
    return source.Skip(pageNr * pageSize).Take(pageSize); 
} 

用法:

db.Posts.Where(p => p.Author == "Bob").OrderBy(p => p.Date).GetPage(5, 10); 

或者,如果你有倉庫

postsRepository.GetByAuthor("Bob").GetPage(5, 10); 
0

試試這個,看看它是否幫助

Expression<Func<TSource, bool>> keySelector 

或者乾脆

Func<TSource, bool> keySelector 
+0

問題是,我沒有Expression >。我有Expression >,我想將它與一個int進行比較,以獲取表達式>。 –

0

鑑於此代碼:

sealed class SwapVisitor : ExpressionVisitor 
{ 
    private readonly Expression _from; 
    private readonly Expression _to; 

    public SwapVisitor(Expression from, Expression to) 
    { 
     _from = from; 
     _to = to; 
    } 

    public override Expression Visit(Expression node) 
    { 
     return node == _from ? _to : base.Visit(node); 
    } 
} 

static Expression<Func<TInput, bool>> Combine<TInput, TOutput>(
    Expression<Func<TInput, TOutput>> transform, 
    Expression<Func<TOutput, bool>> predicate) 
{ 
    var swap = new SwapVisitor(predicate.Parameters[0], transform.Body); 
    return Expression.Lambda<Func<TInput, bool>>(
     swap.Visit(predicate.Body), transform.Parameters); 
} 

您可以:

.Where(Combine(keySelector, key => key == comparisonValue)) 

所以這是創建一個新Expression,與通過表達keySelector和身體新的表達爲比較。

由於Combine Lambda Expressions

2

如何使用KeySelector轉換成適合於謂語?

這很容易,但我不知道你將如何處理排序。總之,這裏是你如何能做到你有什麼要求:

public static IQueryable<TSource> GetPage<TSource, TKey>(this IQueryable<TSource> source, 
    int pageNr, int pageSize, 
    Expression<Func<TSource, TKey>> keySelector, TKey comparisonValue) 
{ 
    var predicate = Expression.Lambda<Func<TSource, bool>>(
     Expression.Equal(keySelector.Body, Expression.Constant(comparisonValue)), 
     keySelector.Parameters); 

    return source 
     .Where(predicate) 
     //.OrderBy(some sortorder) ?? 
     .Skip(pageNr * pageSize) 
     .Take(pageSize); 
} 
相關問題