6

LINQ to Entities エンティティのリポジトリを生成するために T4 を使用しています。

リポジトリには、(特に) ページングに適した List メソッドが含まれています。サポートされているメソッドとサポートされていないメソッドのドキュメントには記載されていませんがSkip、 unordered で「呼び出す」ことはできませんIQueryable。次の例外が発生します。

System.NotSupportedException: メソッド 'Skip' は、LINQ to Entities の並べ替えられた入力に対してのみサポートされています。メソッド「OrderBy」は、メソッド「Skip」の前に呼び出す必要があります。

部分メソッドを介してデフォルトの並べ替えを定義できるようにすることで解決しました。しかし、式ツリーに実際にOrderBy.

問題を可能な限り少ないコードに減らしました。

public partial class Repository
{
    partial void ProvideDefaultSorting(ref IQueryable<Category> currentQuery);

    public IQueryable<Category> List(int startIndex, int count)
    {
        IQueryable<Category> query = List();
        ProvideDefaultSorting(ref query);
        if (!IsSorted(query))
        {
            query = query.OrderBy(c => c.CategoryID);
        }
        return query.Skip(startIndex).Take(count);
    }
    public IQueryable<Category> List(string sortExpression, int startIndex, int count)
    {
           return List(sortExpression).Skip(startIndex).Take(count);
    }
    public IQueryable<Category> List(string sortExpression)
    {
        return AddSortingToTheExpressionTree(List(), sortExpression);
    }
    public IQueryable<Category> List()
    {
           NorthwindEntities ent = new NorthwindEntities();
           return ent.Categories;
    }

    private Boolean IsSorted(IQueryable<Category> query)
    {
        return query is IOrderedQueryable<Category>; 
    }
}

public partial class Repository
{
    partial void ProvideDefaultSorting(ref IQueryable<Category> currentQuery)
    {
        currentQuery = currentQuery.Where(c => c.CategoryName.Contains(" ")); // no sorting..
    }
}

これは私の実際の実装ではありません!

しかし、私の質問は、どのようにIsSortedメソッドを実装できるでしょうか? 問題は、LINQ to Entities クエリが常にObjectQueryを実装するタイプであるということIOrderedQueryableです。

OrderByでは、メソッドが式ツリーに存在することを確認するにはどうすればよいでしょうか? ツリーを解析する唯一のオプションはありますか?

更新リポジトリに並べ替えサポートを追加する方法ではなく、部分メソッドが実際に式ツリーに追加
したかどうかを確認する方法であることを明確にするために、他の 2 つのオーバーロードを追加しました。ProvideDefaultSortingOrderBy

問題は、最初の部分クラスがテンプレートによって生成され、部分クラスの 2 番目の部分の実装が別の時点でチーム メンバーによって行われることです。.NET Entity Framework が EntityContext を生成する方法と比較できます。これにより、他の開発者向けの拡張ポイントが可能になります。ProvideDefaultSortingそのため、が正しく実装されていない場合でも、堅牢にしてクラッシュしないようにしたいと考えています。

問題はそれ以上かもしれませんが、ProvideDefaultSorting実際に式ツリーに並べ替えが追加されたことを確認するにはどうすればよいでしょうか。

更新 2
新しい質問が回答され、受け入れられました。タイトルを質問にもっと一致するように変更する必要があると思います。それとも、同じ問題を抱えている人をこの解決策に導くので、現在のタイトルを残す必要がありますか?

4

6 に答える 6

2

ページングは​​、注文に強く依存します。操作を緊密に結合してみませんか?これを行う1つの方法は次のとおりです。

サポートオブジェクト

public interface IOrderByExpression<T>
{
  ApplyOrdering(ref IQueryable<T> query);
}

public class OrderByExpression<T, U> : IOrderByExpression<T>
{
  public IQueryable<T> ApplyOrderBy(ref IQueryable<T> query)
  {
    query = query.OrderBy(exp);
  }
  //TODO OrderByDescending, ThenBy, ThenByDescending methods.

  private Expression<Func<T, U>> exp = null;

  //TODO bool descending?
  public OrderByExpression (Expression<Func<T, U>> myExpression)
  {
    exp = myExpression;
  }
}

議論中の方法:

public IQueryable<Category> List(int startIndex, int count, IOrderByExpression<Category> ordering)
{
    NorthwindEntities ent = new NorthwindEntities();
    IQueryable<Category> query = ent.Categories;
    if (ordering == null)
    {
      ordering = new OrderByExpression<Category, int>(c => c.CategoryID)
    }
    ordering.ApplyOrdering(ref query);

    return query.Skip(startIndex).Take(count);
}

しばらくして、メソッドを呼び出します。

var query = List(20, 20, new OrderByExpression<Category, string>(c => c.CategoryName));
于 2008-10-22T13:36:35.707 に答える
1

それよりも少し難しいと思います。おわかりのように、Entity Framework は、特定の状況では、OrderBy を黙って無視します。したがって、式ツリーで OrderBy を探すだけでは十分ではありません。OrderBy は「適切な」場所にある必要があり、「適切な」場所の定義は Entity Framework の実装の詳細です。

もうお察しのとおり、私はあなたと同じ場所にいます。エンティティ リポジトリ パターンを使用して、プレゼンテーション レイヤーでテイク/スキップを実行しています。私が使用した解決策は、おそらく理想的ではありませんが、私がやっていることには十分ですが、可能な限り最後の瞬間まで順序付けを行わず、OrderBy が常に式ツリーの最後になるようにすることです。したがって、Take/Skip を (直接的または間接的に) 実行するアクションは、最初に OrderBy を挿入します。コードは、これが 1 回しか発生しないように構成されています。

于 2008-10-22T13:10:03.733 に答える
1

これは、ProvideDefaultSorting の戻り値の型で対処できます。このコードはビルドされません:

    public IOrderedQueryable<int> GetOrderedQueryable()
    {
        IQueryable<int> myInts = new List<int>() { 3, 4, 1, 2 }.AsQueryable<int>();
        return myInts.Where(i => i == 2);
    }

このコードはビルドされますが、潜行性があり、コーダーはそれに値するものを手に入れます。

    public IOrderedQueryable<int> GetOrderedQueryable()
    {
        IQueryable<int> myInts = new List<int>() { 3, 4, 1, 2 }.AsQueryable<int>();
        return myInts.Where(i => i == 2) as IOrderedQueryable<int>;
    }

ref と同じストーリー (これはビルドされません):

    public void GetOrderedQueryable(ref IOrderedQueryable<int> query)
    {
        query = query.Where(i => i == 2);
    }
于 2008-10-22T17:02:17.180 に答える
1

David B のおかげで、次の解決策が得られました。(部分メソッドが実行されていないか、単にパラメーターが返されたという状況の検出を追加する必要がありました)。

public partial class Repository
{
    partial void ProvideDefaultSorting(ref IOrderedQueryable<Category> currentQuery);

    public IQueryable<Category> List(int startIndex, int count)
    {
        NorthwindEntities ent = new NorthwindEntities();
        IOrderedQueryable<Category> query = ent.CategorySet;
        var oldQuery = query;
        ProvideDefaultSorting(ref query);
        if (oldQuery.Equals(query)) // the partial method did nothing with the query, or just didn't exist
        {
            query = query.OrderBy(c => c.CategoryID);
        }
        return query.Skip(startIndex).Take(count);
    }
    // the rest..        
}

public partial class Repository
{
    partial void ProvideDefaultSorting(ref IOrderedQueryable<Category> currentQuery)
    {
        currentQuery = currentQuery.Where(c => c.CategoryName.Contains(" ")).OrderBy(c => c.CategoryName); // compile time forced sotring
    }
}

コンパイル時に、部分メソッドが実装されている場合は、少なくとも IOrderdQueryable を保持する必要があります。

また、部分メソッドが実装されていないか、そのパラメーターを返すだけの場合、クエリは変更されず、フォールバック ソートが使用されます。

于 2008-10-22T19:13:31.097 に答える
0
    ProvideDefaultSorting(ref query);
    if (!IsSorted(query))
    {
            query = query.OrderBy(c => c.CategoryID);
    }

への変更:

    //apply a default ordering
    query = query.OrderBy(c => c.CategoryID);
    //add to the ordering
    ProvideDefaultSorting(ref query);

それは完璧な解決策ではありません。

あなたが述べた「順序付け機能のフィルター」の問題は解決しません。「注文を実装するのを忘れた」または「注文しないことを選択した」を解決します。

LinqToSql でこのソリューションをテストしました。

    public void OrderManyTimes()
    {
        DataClasses1DataContext myDC = new DataClasses1DataContext();
        var query = myDC.Customers.OrderBy(c => c.Field3);
        query = query.OrderBy(c => c.Field2);
        query = query.OrderBy(c => c.Field1);

        Console.WriteLine(myDC.GetCommand(query).CommandText);

    }

生成します (順序の逆順に注意してください):

SELECT Field1, Field2, Field3
FROM [dbo].[Customers] AS [t0]
ORDER BY [t0].[Field1], [t0].[Field2], [t0].[Field3]
于 2008-10-22T15:39:33.510 に答える
0

デフォルトのソート順が指定されていないため、コレクションを主キーでソートするソリューションを実装しました。おそらくそれはあなたのために働くでしょう。

http://johnkaster.wordpress.com/2011/05/19/a-bug-fix-for-system-linq-dynamic-and-a-solution-for-the-entity-framework-4-skip-problemを参照してください。 /ディスカッションと汎用コード用。(そして、Dynamic LINQ の偶発的なバグ修正。)

于 2011-05-19T17:19:46.330 に答える