2016-10-29 10 views
1

は、私は、同じ特定の条件(where節)何回もチェックする必要があります制限されたプロジェクトを取得する。Entity Frameworkの拡張メソッドのICollectionを/のIQueryable

このチェックを関数に抽象化したいと考えました。将来、これらの条件が変わる可能性があり、すべての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クエリを変更することができます。これは、同じSQLクエリを生成

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

。今私の問題は、プロジェクトを持つ別のDbSetでこの拡張メソッドを使用したいときに始まります。

企業にはプロジェクトがあるとします。そして私は、ユーザーが少なくとも1つのプロジェクトを見ることができる会社を検索したいだけです。

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 Entity Frameworkのコアを使用してい

「System.Linq.Queryable:Expression<Func<>>を使用して

は同じ例外が発生しました。 AsQueryable 'は現在サポートされていません。

UPDATEソリューションを提供するための2

Thxを@イワン-stoev。 私はまだ1つの問題があります。私はまた、「目に見える」プロジェクトの数を取得したい。

私はこれを行うことによって、それを修正:

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))  
       }); 

しかし、私はちょうど生成されますSQLが正しいですが、私がしたい ctx.Projects.Where(p => p.CompanyId == c.Id)

の代わりにc.Projectsを使用する方法を見つけることができません。この不要なチェックを避けるためです。敬具

IQueryable<T>クエリ式は、常に問題となって、一部の式ツリーの後処理を必要としている内部の表現/カスタムメソッドを使用して ブレヒト

答えて

5

。例えば、LinqKitは、その目的のためのカスタム拡張方法であるAsExpandable,InvokeおよびExpandを提供します。

一般的ではありませんが、ここではサードパーティのパッケージを使用しないサンプルユースケースの解決策を示します。

まず、メソッドの述語の式部分を抽出します。このようなカスタム拡張メソッドを作成、その後

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); 
    } 
} 

:論理的な場所は、IMO Projectクラスであり、この設計では

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)); 

更新:このソリューションは、あなたが(特に2番目の更新のように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(式合成)のために見る必要があります。 –

関連する問題