web-dev-qa-db-ja.com

Entity Framework Coreで計算されたメンバーを作成する

Entity Framework Core 3とSqlServerデータベースを使用しています。ストレージ、高いクエリコストなどのため、ビジネスプログラムはデータベースにない多くの列を作成する必要があります。現在、チームはデータベースレイヤー全体をコピーし、新しいエンティティに計算されたメンバーを追加する別のレイヤー全体を作成しています。現在データベースレイヤーを取得して、AutoMapperを新しいレイヤーに適用しています。何らかの理由で、これは最適な方法のようには見えません。

この例では、計算されたメンバーが必要です

FullName => FirstName + LastName

AccountValue => Quantity * StockPrice

Entity Framework 3はクライアント側の評価を許可しなくなりました https://devblogs.Microsoft.com/dotnet/announcing-ef-core-3-0-and-ef-6-3-general-availability/ Entity Framework Core 3の計算されたメンバーの標準化された方法は何ですか?

この記事を読んで、何が最新で何が使えるのか不思議に思っていますか?または、Entity Framework Core 3は新しい構文を提供しますか?

https://daveaglick.com/posts/computed-properties-and-entity-framework

1)エンティティを具体化できます。大丈夫のようですが、これにより、開発者はToList()を利用することを覚えておく必要があり、開発者が忘れる問題があり、長いdbスキャンクエリが発生したり、クライアント側の評価がエラーを引き起こしたりしました。

var result = ctx.Customers
  .ToList()
  .Select(c => new
  {
    FullName = c.FullName,
    AccountValue = c.AccountValue
  });

2)Queryable Extensionを作成します。これは計算された列のみを抽出するか、開発者に計算されたメンバーをすべて1つのクラスで作成するよう強制します(SRPの単一責任の概念を破ります)。これに対処する別の変更がない限り。これには、コンポジションチェーンの問題と、オプション1のようなパフォーマンスの問題が発生する可能性もあります。

public static IQueryable<CustomerData> SelectCustomerData(this IQueryable<Customer> customers) {   return customers.Select(c => new CustomerData   {
    FullName = c.FirstName + " " + c.LastName,
    AccountValue = c.Holdings.Sum(h => h.Quantity * h.Stock.Price)   }); }

3)式の投影。Linq式のプロジェクトなしでSelectで割り当てを行うことはできません。 Microsoftからビルドされていない限り、このサードパーティツールは許可されません。

public readonly Expression<Func<Customer, decimal>> AccountValueExpression = c => c.Holdings.Sum(h => h.Quantity * h.Stock.Price);

または、Entity Framework Core 3は新しい構文を提供しますか?

ソリューションは、(a)元のDBEntityの既存のメンバーの一部またはすべてを抽出できる場所、(b)新しいメンバーの一部またはすべて、

例、FirstName(既存)とAccountValue(新しいメンバー)が必要

または、FullName、FirstName、LastName、StockPrice、

またはすべて、FirstName、LastName、FullName、Quantity、StockPrice、AccountValueなど

エンティティのあらゆる組み合わせまたは一致。

実際には2.2からCore 3に移行していますが、2.2ではClientSide Evaluationが無効になっています。 Linq.TranslationsやDelegateCompilerなどのサードパーティツールは、Microsoftベンダーから作成されていない限り利用できません。

私たちはDBAチームに依存しているため、SqlServer計算列を使用しないことをお勧めします。さらに、より複雑な計算があります。

5

まず、ここで EF 3の変更点に関する最新のお知らせ を引用します。

3.0以降、EF Coreでは、トップレベルのプロジェクション(クエリの最後のSelect()呼び出し)の式のみをクライアントで評価できます。クエリの他の部分の式をSQLまたはパラメーターに変換できない場合、例外がスローされます。

次のクエリをテストしたところ、

var list = context.Customers.Include(c => c.Stocks).Select(c => new
{
    FullName = c.FirstName + " " + c.LastName,
    TotalInvestment = c.Stocks.Sum(s => s.Price*s.Quantity)
});
list.ToList();
/*
      SELECT ([c].[FirstName] + N' ') + [c].[LastName] AS [FullName], (
          SELECT SUM([s].[Price] * [s].[Quantity])
          FROM [Stocks] AS [s]
          WHERE [c].[Id] = [s].[CustomerId]) AS [TotalInvestment]
      FROM [Customers] AS [c]
*/

しかし、このトピックをもう少し詳しく見て、クライアント側にすべての評価を行わずに、計算フィールドでテーブルをクエリしたいとします。

var list = context.Customers.Include(c => c.Stocks)
                    .Where(c => string.Concat(string.Concat(c.FirstName, " "), c.LastName) == "John Doe") // notice how we have to do string.Concat twice. observe what hppens if you use the next line instead
                    //.Where(c => string.Concat(c.FirstName, " ", c.LastName) == "John Doe"); // this query will fail to evaluate on SQL and will throw an error, because EF's default concat translator implementation only caters for two parameters
                ;
list.ToList();
/* the following SQL has been generated
      SELECT [c].[Id], [c].[FirstName], [c].[LastName], [s].[Id], [s].[CustomerId], [s].[Price], [s].[Quantity]
      FROM [Customers] AS [c]
      LEFT JOIN [Stocks] AS [s] ON [c].[Id] = [s].[CustomerId]
      WHERE (([c].[FirstName] + N' ') + [c].[LastName]) = N'John Doe'
      ORDER BY [c].[Id], [s].[Id]
*/

どうやら、EF3にはそれを可能にするいくつかのビルド済み関数が付属しています:もう少し取得するにはIMethodCallTranslator実装(たとえば StringMethodTranslator など)を参照してくださいそれがどのように行われているのかについての洞察(そしてあなたがすぐに利用できる他の翻訳者).

さて、あなたの翻訳者が実装されていない場合、私はあなたが尋ねるのを聞きます。これは、物事がもう少しエキサイティングになる場所です。 this SO answer で概説されているように、EF 2.2でこれを正常に実行しました(チェックアウトすることをお勧めします)。残念ながら、コードは直接EF3に変換されません1:1でも、同じアプローチでうまくいくと確信しています。

[〜#〜] upd [〜#〜]:EFで動作するカスタムDB関数のPoCについては my github repo を参照してくださいコア3.1

1
timur

私はこれを試していませんが、それは単なる考えです-ExtensionのカスタムDbFunctionsメソッドを作成してみませんか?

利用した EF.Functions.Likeメソッドは、SQL LIKE操作の実装です。リレーショナルデータベースでは、これは通常SQLに直接変換されます。

これらのリンクを確認してください-

0
Indar-AIS

バックエンドでサポートされていると仮定すると、計算されたプロパティを計算されたデータベース列でミラーリングできます。

    public string FullName => FirstName + LastName;

    entity.Property(e => e.FullName).HasComputedColumnSql("FirstName + LastName");

次に、これらのプロパティを簡単にフィルタリング、並べ替え、プロジェクトなどを行うことができます。

0
Jeremy Lakeman