2016-10-14 2 views
3

LINQクエリに問題があります。クエリは次のようになります。テーブルで定義された5次元の問題空間からListパラメータを使用してLINQクエリをコンパイルする方法はありますか?

 Dim elements = (From filterSum In dataContext.GetTable(Of TblFilterSum) 
         Join filterProdInSum In dataContext.GetTable(Of TblFilterProdsInSum) 
        On filterSum.SumID Equals filterProdInSum.SumID 
         Join filterProd In dataContext.GetTable(Of TblFilterProd) 
        On filterProdInSum.ProdID Equals filterProd.ProdID 
         Join filterElementInProd In dataContext.GetTable(Of TblFilterElementsInProd) 
        On filterProd.ProdID Equals filterElementInProd.ProdID 
         Join filterElement In dataContext.GetTable(Of TblFilterElement) 
        On filterElementInProd.ElementID Equals filterElement.ElementID 
         Where sumIDs.Contains(filterSum.SumID)).Select(Function(r) New With {.SumID = r.filterSum.SumID, .ProdID = r.filterProd.ProdID, .filterElement = r.filterElement, .IsNotSum = r.filterSum.IsNot, .IsNotProd = r.filterProd.IsNot}).ToList 

このクエリ負荷を記録、:

  • tblFilterSum
  • tblFilterProdsInSum
  • tblFilterProd
  • tblFilterElementsInProd
  • tblFilterElement

私はWhere句で使用するフィルタはtblFilterSum.SumIDsumIDs呼ばList(Of Integer)の内側にあるということです。 Linqクエリは論理的に完璧で、私が必要とする結果しかありません。ただし、実行には平均で20秒の実行時間が必要です。これは、LINQクエリを生成SQLです:

-- Region Parameters 
DECLARE @p0 Int = 12168 
DECLARE @p1 Int = 12157 
DECLARE @p2 Int = 11948 
DECLARE @p3 Int = 11951 
DECLARE @p4 Int = 11952 
DECLARE @p5 Int = 11950 
DECLARE @p6 Int = 11961 
DECLARE @p7 Int = 12153 
DECLARE @p8 Int = 12154 
DECLARE @p9 Int = 12149 
DECLARE @p10 Int = 12158 
DECLARE @p11 Int = 11954 
DECLARE @p12 Int = 11955 
DECLARE @p13 Int = 11956 
DECLARE @p14 Int = 11957 
DECLARE @p15 Int = 11958 
DECLARE @p16 Int = 11959 
DECLARE @p17 Int = 12159 
DECLARE @p18 Int = 12164 
DECLARE @p19 Int = 12150 
DECLARE @p20 Int = 12151 
DECLARE @p21 Int = 12152 
DECLARE @p22 Int = 12156 
DECLARE @p23 Int = 12161 
DECLARE @p24 Int = 12167 
DECLARE @p25 Int = 11962 
DECLARE @p26 Int = 12155 
DECLARE @p27 Int = 12183 
DECLARE @p28 Int = 12182 
DECLARE @p29 Int = 12165 
DECLARE @p30 Int = 12166 
DECLARE @p31 Int = 11953 
DECLARE @p32 Int = 12163 
DECLARE @p33 Int = 12181 
DECLARE @p34 Int = 12180 
DECLARE @p35 Int = 12160 
DECLARE @p36 Int = 12162 
-- EndRegion 
SELECT [t0].[SumID], [t2].[ProdID], [t4].[ElementID], [t4].[Field], [t4].[Operator], [t4].[Result], [t4].[IsCustom], [t4].[IsNot], [t4].[DisplayOrder], [t4].[FilteredColumnID], [t4].[ColumnMappingID], [t4].[ResultPointerToAssetPath], [t4].[ResultCustomColumnMappingID], [t0].[IsNot] AS [IsNotSum], [t2].[IsNot] AS [IsNotProd] 
FROM [dbo].[tblFilterSum] AS [t0] 
INNER JOIN [dbo].[tblFilterProdsInSum] AS [t1] ON [t0].[SumID] = [t1].[SumID] 
INNER JOIN [dbo].[tblFilterProd] AS [t2] ON [t1].[ProdID] = [t2].[ProdID] 
INNER JOIN [dbo].[tblFilterElementsInProd] AS [t3] ON [t2].[ProdID] = [t3].[ProdID] 
INNER JOIN [dbo].[tblFilterElement] AS [t4] ON [t3].[ElementID] = [t4].[ElementID] 
WHERE [t0].[SumID] IN (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16, @p17, @p18, @p19, @p20, @p21, @p22, @p23, @p24, @p25, @p26, @p27, @p28, @p29, @p30, @p31, @p32, @p33, @p34, @p35, @p36) 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 4.6.1055.0 
GO 

これは私がゆえの問題は、LINQの下には、.NET Frameworkの4.0は、クエリにそれが実行されるたびにコンパイルすることで、直接それを実行した場合、即座に実行され、何のキャッシュがありませんまったく。ただし、結果をキャッシュする可能性があります。System.Data.Linq.CompiledQuery.Compileは、LINQクエリをコンパイルできる便利な方法です。返されるFuncは、.NET Framework 4.0で使用されるalways-compile戦略を回避するために、さまざまなパラメータで何度も何度も再利用できます。考えられるそれぞれのクエリに対して一意の署名を作成し、共有ディクショナリを使用して、あらゆる種類のクエリを一度コンパイルし、キャッシュに格納して後で他の属性と再利用することができます。これは良いとは言えますが、可能な用途を見てみましょう:

Public Shared Function Compile(Of TArg0 As DataContext, TResult)(query As Expression(Of Func(Of TArg0, TResult))) As Func(Of TArg0, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TResult))) As Func(Of TArg0, TArg1, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TResult))) As Func(Of TArg0, TArg1, TArg2, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TArg5, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TArg14, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TArg14, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TArg14, TResult) 

Public Shared Function Compile(Of TArg0 As DataContext, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TArg14, TArg15, TResult)(query As Expression(Of Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TArg14, TArg15, TResult))) As Func(Of TArg0, TArg1, TArg2, TArg3, TArg4, TArg5, TArg6, TArg7, TArg8, TArg9, TArg10, TArg11, TArg12, TArg13, TArg14, TArg15, TResult) 

私はすぐに2つの問題があることを認識しました。一度は、Compileメソッドは、異なる可能なパラメータ値と同じ数のパラメータを持つオーバーロードを返します。私が持っていた要素の数ごとにコンパイルされたクエリをキャッシュすることができるので、それほど深刻な問題ではありません。それはいくらかの記憶を無駄にするが、それほど多くはない。あまりにも多くなったら、私はレディスと一緒にそれを保管しますが、それは別の問題です。実際の問題は、パラメータの数が16を超えることはできず、私のListは簡単に多くの要素を含むことができるということです。

,5,2,7,34,764,346,1, 

このStringが含まれているかどうかを確認する:この問題に、解決策を開始し、そのセパレータで終わる、このたい各番号と文字列との間のセパレータと、StringsumIDsを変換することであろう与えられたtblFilterSum.SumIDは次のようにStringに変換:

"," & tblFilterSum.SumID & "," 

が、これは仕事だろうが、インデックスを使用することはできません、それは非常に遅くなる文字列を比較します。私はこれらの問題があり、作業が緊急だったので、これをSQLで実装しましたが、LINQクエリをコンパイルして、任意の多くのパラメータをListとして渡すことができるかどうか疑問に思います。したがって、私の質問:

Listをパラメータとして使用してLINQクエリをコンパイルする方法はありますか?

編集:私はLINQのクリエイターに連絡する方法を知っていた場合は

、その後、私は答えを自分で見つけることができ信じています。私はGoogleでLINQを検索しましたが、残念ながら連絡先情報が見つかりませんでした。

答えて

1

Contains()cannot be automatically cachedを使用しているクエリ。したがって、解決方法はContainsメソッドを避けることです。これを行うには、Linq Expressions APIを使用して同等の式(f => f.SumID == x || f.SumId == y ...)を作成する必要があります。

次のソリューションは、netfxのExpressionCombinerを使用しています。これはC#であり、VBへの変換は簡単でなければなりません。このコードは、今、あなたのクエリで使用することができます

public static IQueryable<T> WherePropertyIn<T, TProp>(this IQueryable<T> src, Expression<Func<T, TProp>> property, IEnumerable<TProp> values) { 
    var valuesList = values.ToList(); 
    // If no values passed, then nothing matches 
    if (!valuesList.Any()) { 
     return src.Where(_ => false); 
    } 
    // This builds the 'f => f.Prop == x || f.Prop == y ...' expression 
    Expression<Func<T, bool>> expr = valuesList 
             .Select(val => property.EqualTo(val)) // Here we have a list of 'f => f.Prop == x' style expressions 
             .Aggregate(ExpressionCombiner.Or); // And combine them with || 
    return src.Where(expr); 
} 

// Creates an expression 'f => f.Prop == val' out of expression 'f => f.Prop' and value 'val' 
private static Expression<Func<T, bool>> EqualTo<T, TProperty>(this Expression<Func<T, TProperty>> leftHand, TProperty val) { 
    // If we don't wrap in property access, LINQ to Entities uses the value directly instead of via a sql parameter, thus breaking caching. 
    Expression rightHand = Expression.Constant(val, typeof(TProperty)).WrapInPropertyAccess(); 
    Expression comparison = Expression.Equal(leftHand.Body, rightHand); 
    return Expression.Lambda<Func<T, bool>>(comparison, leftHand.Parameters); 
} 

/// <summary> 
/// Returns an expression around the ConstantExpression, enabling LINQ to Entities to generate parameterized queries 
/// </summary> 
/// <param name="constant"></param> 
/// <returns></returns> 
private static UnaryExpression WrapInPropertyAccess(this ConstantExpression constant) { 
    Tuple<object> container = new Tuple<object>(constant.Value); 
    Expression containerExpression = Expression.Constant(container, typeof(Tuple<object>)); 
    MemberExpression propAccess = Expression.Property(containerExpression, "Item1"); 
    UnaryExpression result = Expression.Convert(propAccess, constant.Type); // Cast back the object to the value type 
    return result; 
} 

Dim filterSumSubQuery = dataContext.GetTable(Of TblFilterSum).WherePropertyIn(Function(t) r.SumID, sumIDs) 
' Subquery now contains only the TbFilterSum tuples that have the right SumIDs 
+0

フェリペは、あなたの答えをありがとう、しかし、あなたは質問を理解していないように見えます。 (残念ながら)私は.NET Framework 4.0を使用しているため、Entity Framework 5+は.NET 4.5+を依存関係として使用できません。自動キャッシュは、私のケースでは使用できないEntity Framework 5+の機能であるため、私たちが話している環境では自動キャッシュはまったく不可能です。自分の状況では自動キャッシュが不可能なので、私は手動キャッシュを行うつもりであり、Listで作業する必要があります。これが問題なのです。 –

+0

私の場合は自動キャッシュができないので(.NET 4が含まれているタグからわかるように)、私は手動でキャッシュするつもりです(質問から分かるように)。問題はCompiledQuery.Compileがlistパラメータをサポートし、有限個の可能なパラメータを持ち、反射スタイルのパラメータが無駄になります。あなたは、自動コンパイルを可能にするためにカスタムに何かが含まれていることを示唆していますが、それは到達不能な機能です。 –

+0

ああ、私はあなたがEF 5+を使うことができないことに気付かなかった。その質問にその旨のフレーズを追加することをお勧めします。私はあなたの問題を解決する方法がわかりません:( – felipe

関連する問題