テーブルの正確なリソースを表す次のエンティティがあるとします。ここでは、.NET CoreとEntity Framework Coreのコードファーストアプローチについて説明します。
public class Person
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
一般的に言えば、このクラスからDTOを返すとき、姓と名を結合するFullNameと呼ばれる1つのフィールドだけを返すことにするかもしれませんが、そのロジックを複製するのではなく、Personでメソッドを宣言するだけの考えはどれほど悪いですか?
public string GetFullName()
{
return $"{FirstName} {LastName}";
}
私の見解では、これはコード・ファーストEFの意図です。あなたが持っていない DTOあなたは魔法であなたのビジネスオブジェクトを単に永続化します。
残念ながら、私はそれがうまく機能するとは思いません。たとえば、メソッドの代わりにFullName計算プロパティを追加する場合、EFプロパティを追加して、そのプロパティの処理方法を指示する必要があります。
属性を追加すると、オブジェクトがデータレイヤーと半分のEF DLLにしっかりと結合されます。
EFを使用する「ベストプラクティス」の方法は、EFをリポジトリクラスの背後に隠すことです。しかし、これはすべてではないにしても、その利点のほとんどを打ち消します。
EFは、データベースのVB6印刷プレビューです。数回クリックするだけでDBの永続化を実行できますが、より複雑なケースに到達すると、自分で最初から実行する方が簡単です
これは、アプリケーションの layering に依存します。また、アプリケーションに特定のORMテクノロジーに関連付けられていないビジネスレイヤーが必要であると思われる場合、またはそのようなレイヤー分離が必要ではないと考える場合必要。
通常、 1つのレイヤーのいずれかを決定します。ここで、「ビジネスオブジェクト」はデータベーステーブルに直接マップされ、特定のビジネスロジックが含まれます (メソッドGetFullName
のように)。これは、少数のテーブルと少数のビジネスクラスしかなく、それらの間の構造的な違いがあまりない小規模なアプリケーションにはおそらく十分です。
または、 2つのレイヤーを持つことを決定し、永続化のEFコードファーストのアプローチに使用されるクラスPersonDTO
とビジネスクラスを完全に分離します。 Person
はEFから独立していますが、ビジネスロジックを保持しています。この場合、Person
はGetFullName
のようなメソッドを取得し、PersonDTO
に同じロジックを提供するとそれが複製されます。これは、実際に回避方法を尋ねたものです。
これはトレードオフであることは明らかだと思います-前者のアプローチでは必要なコードが少なくなりますが、ビジネスオブジェクトを使用するすべてのコードはEFにも依存する必要があり、ビジネスクラスがどのように行う必要があるかについて特定の制限を課しますのように見える。後者は、PersonDTO
とPerson
の間のマッピングに追加のコードを必要とします(これが Automapper などのツールが値を表示する場所です)。
したがって、最初にどのような種類のアプリケーションを作成するかを検討することをお勧めします。DTOとビジネスレイヤーの間のレイヤー分離が理にかなっているかどうかを判断します。次に、メソッドを配置する場所がわかります。
私の意見では、エンティティをコンシューマーに送信する前に、常にエンティティを投影することをお勧めします。プロジェクションを再利用する方法はいくつかあります。最も簡単な方法は、拡張メソッドを使用することです。
internal static class PersonExtensions
{
public static IQueryable<PersonInfo> Project(this IQueryable<Person> source)
{
return source.Select(p => new PersonInfo { ... }
}
}
プロジェクト選択から使用される式を再利用することもできます。 EF Coreでは、yuoはかなり優れた別の式の一部として式を使用することもできますが、通常の式を使用すると自動的に解決されるナビゲーションプロパティを明示的に含める必要があるという欠点があります。
private static readonly Expression<Func<Person, PersonInfo>> Projection = p => new PersonInfo { ... };
private static Func<Person, PersonInfo> CompiledProjection = Projection.Compile();
次に、例えば、
.source
.Include(p => p.navProp)
.Select(p => new
{
dto = Compiled.Invoke(p),
children = p.Children.AsQueryable().Select(Projection)
}