2016-10-29 64 views
1

我需要檢查相同的特定條件(where條款)多次:實體框架的擴展方法ICollection/IQueryable的

return _ctx.Projects.Where(p => p.CompanyId == companyId && 
          (p.Type == Enums.ProjectType.Open || 
          p.Invites.Any(i => i.InviteeId == userId))).ToList() 

的「& &」後面的部分會導致用戶不能檢索受限項目。

我想抽象這個檢查功能。將來這些條件可能會改變,我不想替換所有的LINQ查詢。

我這樣做有以下擴展方法:

public static IQueryable<Project> IsVisibleForResearcher(this IQueryable<Project> projects, string userId) 
{ 
    return projects.Where(p => p.Type == Enums.ProjectType.Open || 
           p.Invites.Any(i => i.InviteeId == userId)); 
} 

現在我可以LINQ查詢更改爲:

return _ctx.Projects.Where(p => p.CompanyId == companyId) 
        .IsVisibleForResearcher(userId).ToList() 

這會產生相同的SQL查詢。現在我的問題開始時,我想在另一個具有項目的DbSet上使用此擴展方法。

想象一下,一家公司有項目。我只想找回那些用戶至少可以看到一個項目的公司。

return _ctx.Companies 
     .Where(c => c.Projects.Where(p => 
      p.Type == Enums.ProjectType.Open || 
      p.Invites.Any(i => i.InviteeId == userId)) 
     .Any()) 

這裏我也喜歡用擴展方法。

return _ctx.Companies 
     .Where(c => c.Projects.AsQueryable().IsVisibleForCompanyAccount(userId).Any()) 

這將引發以下異常:

的 'System.NotSupportedException' 發生在 Remotion.Linq.dll但在用戶代碼中沒有處理

信息類型的異常:莫非未解析表達式「c.Projects.AsQueryable()」:當前不支持該方法「System.Linq.Queryable.AsQueryable」的這一過載。

比我創建了下面的擴展方法:

public static IEnumerable<Project> IsVisibleForResearcher(this ICollection<Project> projects, string userId) 
{ 
    return projects.Where(p => p.Type == Enums.ProjectType.Open || 
           p.Invites.Any(i => i.InviteeId == userId)); 
} 

但這也沒工作。

有沒有人有想法? 或朝着正確的方向邁出的一步。

順便說一下我使用實體框架的核心.NET的核心

UPDATE

使用Expression<Func<>>導致相同的異常:

「System.Linq.Queryable。 AsQueryable'目前不支持。

UPDATE 2

THX @伊凡-stoev用於提供一個解決方案。 我仍然有一個問題。我也想檢索「可見」項目的數量。

我固定它通過這樣做:

var companies = _ctx.Companies 
       .WhereAny(c => c.Projects, Project.IsProjectVisibleForResearcher(userId)) 
       .Select(c => new CompanyListDto 
       { 
        Id = c.Id, 
        Name = c.Name, 
        LogoId = c.LogoId,     
        ProjectCount = _ctx.Projects.Where(p => p.CompanyId == c.Id) 
             .Count(Project.IsProjectVisibleForResearcher(userId))  
       }); 

但我沒有找到一個方法來只使用c.Projects代替ctx.Projects.Where(p => p.CompanyId == c.Id)

獲取生成是正確的SQL的,但我想以避免這種不需​​要的檢查。

真誠, 布萊希特

回答

5

使用表達式/在IQueryable<T>查詢表達式中的自定義方法一直是他們有問題的,需要一些表達式樹後處理。例如,LinqKit爲此提供AsExpandable,InvokeExpand自定義擴展方法。

儘管不是很一般,但這裏是您使用第三方軟件包的示例用例的解決方案。

首先,在方法中提取謂詞的表達式部分。邏輯的地方IMO是Project類:

public class Project 
{ 
    // ... 
    public static Expression<Func<Project, bool>> IsVisibleForResearcher(string userId) 
    { 
     return p => p.Type == Enums.ProjectType.Open || 
        p.Invites.Any(i => i.InviteeId == userId); 
    } 
} 

然後,創建一個自定義的擴展方法是這樣的:

public static class QueryableExtensions 
{ 
    public static IQueryable<T> WhereAny<T, E>(this IQueryable<T> source, Expression<Func<T, IEnumerable<E>>> elements, Expression<Func<E, bool>> predicate) 
    { 
     var body = Expression.Call(
      typeof(Enumerable), "Any", new Type[] { typeof(E) }, 
      elements.Body, predicate); 
     return source.Where(Expression.Lambda<Func<T, bool>>(body, elements.Parameters)); 
    } 
} 

採用這種設計,沒有必要當前的擴展方法,因爲Projects查詢可以使用:

var projects = _ctx.Projects 
    .Where(p => p.CompanyId == companyId) 
    .Where(Project.IsVisibleForResearcher(userId)); 

Companies

var companies = _ctx.Companies 
    .WhereAny(c => c.Projects, Project.IsVisibleForResearcher(userId)); 

更新:該解決方案是相當有限的,所以如果你有不同的使用情況(特別是Select表達式作爲你的第二個更新中),你最好求助於一些第三方軟件包。例如,這裏是LinqKit解決方案:

// LInqKit requires expressions to be put into variables 
var projects = Linq.Expr((Company c) => c.Projects); 
var projectFilter = Project.IsVisibleForResearcher(userId); 
var companies = db.Companies.AsExpandable() 
    .Where(c => projects.Invoke(c).Any(p => projectFilter.Invoke(p))) 
    .Select(c => new CompanyListDto 
    { 
     Id = c.Id, 
     Name = c.Name, 
     LogoId = c.LogoId, 
     ProjectCount = projects.Invoke(c).Count(p => projectFilter.Invoke(p)) 
    }); 
+0

完全有效!非常感謝您的回答!祝你今天愉快! 有點想研究解決方案,所以下次我需要它時,我可以自己生成這樣的東西。 – ErazerBrecht

+0

還有一個小問題。我在原始問題中將其作爲「更新2」發佈。生成的SQL是正確的,但我不喜歡我必須這樣做的方式。不要覺得自己有義務檢查! – ErazerBrecht

+1

檢查它沒有問題。問題在於解決方案非常有限(正如我在開始時提到的) - 僅適用於頂級查詢。一旦你需要另一種方法(嵌套'Where','Count'等),特別是在你的更新中的'Select'內部,你會發現你需要越來越多的助手。可能你真的必須看看用於投影的AutoMapper('ProjectTo')和/或用於表達式組合的LinqKit。 –