簡體   English   中英

使用Where(表達式 <Func<T, bool> &gt;)在IGrouping中

[英]Using Where( Expression<Func<T, bool>> ) in IGrouping

考慮以下Linq to Entities查詢:

return (from lead in db.Leads
    join postcodeEnProvincie in postcodeEnProvincies
    on lead.Postcode equals postcodeEnProvincie.Postcode
    where (lead.CreationDate >= range.StartDate) && (lead.CreationDate <= range.EndDate)
    group lead by postcodeEnProvincie.Provincie into g
    select new Web.Models.GroupedLeads() {
        GroupName = g.Key,
        HotLeads = g.Count(l => l.Type == Data.LeadType.Hot),
        Leads = g.Count(),
        PriorityLeads = g.Count(l => l.Type == Data.LeadType.Priority),
        Sales = g.Count(l => l.Sold),
        ProductA = g.Count(l => l.Producten.Any(a => ((a.Name.Equals("productA", StringComparison.CurrentCultureIgnoreCase)) || (a.Parent.Name.Equals("productA", StringComparison.CurrentCultureIgnoreCase))))),
        ProductB = g.Count(l => l.Producten.Any(a => ((a.Name.Equals("productB", StringComparison.CurrentCultureIgnoreCase)) || (a.Parent.Name.Equals("productB", StringComparison.CurrentCultureIgnoreCase))))),
        ProductC = g.Count(l => l.Producten.Any(a => ((a.Name.Equals("productC", StringComparison.CurrentCultureIgnoreCase)) || (a.Parent.Name.Equals("productC", StringComparison.CurrentCultureIgnoreCase))))),
        ProductC = g.Count(l => l.Producten.Any(a => ((a.Name.Equals("productD", StringComparison.CurrentCultureIgnoreCase)) || (a.Parent.Name.Equals("productD", StringComparison.CurrentCultureIgnoreCase)))))
}).ToList();

如果你像我一樣,你的腳趾會重復產品選擇邏輯。 這種模式也在另一個地方重復。 我首先嘗試用IEnumerable上的擴展方法替換它,這當然不起作用:Linq to Entities需要一個Expression來解析和翻譯。
所以我創建了這個方法:

    public static System.Linq.Expressions.Expression<Func<Data.Lead, bool>> ContainingProductEx(string productName)
    {
        var ignoreCase = StringComparison.CurrentCultureIgnoreCase;

        return (Data.Lead lead) =>
            lead.Producten.Any(
                (product =>
                    product.Name.Equals(productName, ignoreCase) ||
                    product.Parent.Name.Equals(productName, ignoreCase)
                ));
    }

以下選擇現在完全正常:

var test = db.Leads.Where(Extensions.ContainingProductEx("productA")).ToList();

但是,這不會編譯,因為IGrouping不包含Where接受Expression的覆蓋:

return (from lead in db.Leads
        join postcodeEnProvincie in postcodeEnProvincies
        on lead.Postcode equals postcodeEnProvincie.Postcode
        where (lead.CreationDate >= range.StartDate) && (lead.CreationDate <= range.EndDate)
        group lead by postcodeEnProvincie.Provincie into g
        select new Web.Models.GroupedLeads()
        {
            GroupName = g.Key,
            HotLeads = g
                .Where(l => l.Type == Data.LeadType.Hot)
                .Count(),
            Leads = g.Count(),
            PriorityLeads = g
                .Where(l => l.Type == Data.LeadType.Priority)
                .Count(),
            Sales = g
                .Where(l => l.Sold)
                .Count(),
            ProductA = g
                .Where(Extensions.ContainingProductEx("productA"))
                .Count(),
            ProductB = g
                .Where(Extensions.ContainingProductEx("productB"))
                .Count(),
            ProductC = g
                .Where(Extensions.ContainingProductEx("productC"))
                .Count(),
            ProductD = g
                .Where(Extensions.ContainingProductEx("productD"))
                .Count()
        }).ToList();

將g轉換為IQueryable編譯,但隨后產生“內部.NET Framework數據提供程序錯誤1025”。

有沒有辦法將這個邏輯包裝在自己的方法中?

這是一個可以使用LINQKit解決的問題。 它允許從其他表達式中調用表達式,並在其調用者中內聯調用的表達式。 遺憾的是,它只支持一些非常具體的情況,所以我們需要調整你的表達式生成方法。

不是將產品名稱傳遞給表達式生成方法,而是將它作為返回表達式的參數:

public static Expression<Func<Data.Lead, string, bool>> ContainingProductEx()
{
    var ignoreCase = StringComparison.CurrentCultureIgnoreCase;

    return (lead, productName) =>
        lead.Producten.Any(
            (product =>
                product.Name.Equals(productName, ignoreCase) ||
                product.Parent.Name.Equals(productName, ignoreCase)
            ));
}

接下來,我們需要在聲明查詢之前調用該方法:

var predicate = Extensions.ContainingProductEx();

您的查詢現在可以寫為:

from lead in db.Leads.AsExpandable()
//...
    ProductA = g
        .Where(lead => predicate.Invoke(lead, "productA"))
        .Count(),
    ProductB = g
        .Where(lead => predicate.Invoke(lead, "productB"))
        .Count(),
    ProductC = g
        .Where(lead => predicate.Invoke(lead, "productC"))
        .Count(),
    ProductD = g
        .Where(lead => predicate.Invoke(lead, "productD"))
        .Count()

而不是擔心在您的查詢中創建一個您可以引用的函數指針/表達式(可能不可能),為什么不創建一個單獨的私有方法,它接受一個IEnumerable<Lead> ,一個字符串,並返回一個int並引用它查詢中的方法組? 我認為您的困惑源於嘗試在集合上創建擴展方法,而不是創建集合中的方法和您正在尋找的值。

就像是:

ProductA = GetLeadsForProduct(g, "productA")

private int GetLeadsForProduct(IEnumerable<Lead> leads, string productType)
{
    return leads.Count(l => l.Producten.Any(a => ((a.Name.Equals(productType, StringComparison.CurrentCultureIgnoreCase)) || (a.Parent.Name.Equals(productType, StringComparison.CurrentCultureIgnoreCase)))))
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM