249

これまでの私の印象は、 aDbContextはデータベースを表すためのものであり、したがって、アプリケーションが 1 つのデータベースを使用する場合、必要なのは 1 つだけDbContextです。

ただし、一部の同僚は、機能領域を別々のDbContextクラスに分割したいと考えています。

これは良いところから来ていると私は信じています - コードをきれいに保ちたいという願望 - しかし、それは揮発性に見えます. 私の直感は、それは悪い考えだと言っていますが、残念ながら、私の直感はデザイン決定の十分条件ではありません.

だから私は探しています:

A) これが悪い考えである理由の具体例。

B)これがすべてうまくいくことを保証します。

4

11 に答える 11

195

単一のデータベースに対して複数のコンテキストを持つことができます。たとえば、データベースに複数のデータベーススキーマが含まれていて、それぞれを個別の自己完結型領域として処理する場合に便利です。

問題は、最初にコードを使用してデータベースを作成する場合です。これを実行できるのは、アプリケーション内の単一のコンテキストのみです。このための秘訣は、通常、データベースの作成にのみ使用されるすべてのエンティティを含む1つの追加コンテキストです。エンティティのサブセットのみを含む実際のアプリケーションコンテキストでは、データベース初期化子をnullに設定する必要があります。

複数のコンテキストタイプを使用する場合に発生する他の問題があります。たとえば、共有エンティティタイプや、あるコンテキストから別のコンテキストへの受け渡しなどです。一般的には、デザインをよりクリーンにし、さまざまな機能領域を分離することができますが、追加の複雑さのコスト。

于 2012-06-25T22:35:30.187 に答える
83

私は約 4 年前にこの回答を書きましたが、私の意見は変わっていません。しかし、それ以来、マイクロサービスの最前線で大きな進展がありました。最後にマイクロサービス固有のメモを追加しました...

私は、私の投票を裏付ける実世界の経験とともに、この考えに反対します。

私は、1 つのデータベースに対して 5 つのコンテキストを持つ大規模なアプリケーションを担当しました。最終的に、1 つを除いてすべてのコンテキストを削除し、単一のコンテキストに戻しました。

最初は、複数のコンテキストのアイデアは良いアイデアのように思えます。データ アクセスをドメインに分離し、いくつかのクリーンで軽量なコンテキストを提供できます。DDDのようですね。これにより、データ アクセスが簡素化されます。別の議論は、必要なコンテキストにのみアクセスするというパフォーマンスに関するものです。

しかし実際には、アプリケーションが成長するにつれて、多くのテーブルがさまざまなコンテキストで関係を共有していました。たとえば、コンテキスト 1 のテーブル A へのクエリでは、コンテキスト 2 のテーブル B も結合する必要がありました。

これにより、いくつかの悪い選択が残されました。さまざまなコンテキストでテーブルを複製できます。これを試しました。これにより、各エンティティに一意の名前を付ける必要がある EF 制約など、いくつかのマッピングの問題が発生しました。そのため、異なるコンテキストで Person1 および Person2 という名前のエンティティが作成されました。これは私たちの設計が悪いと主張する人もいるかもしれませんが、私たちの最善の努力にもかかわらず、これが私たちのアプリケーションが現実の世界で実際に成長した方法です.

また、必要なデータを取得するために、両方のコンテキストを照会してみました。たとえば、私たちのビジネス ロジックは、コンテキスト 1 から必要なものの半分をクエリし、コンテキスト 2 から残りの半分をクエリします。これにはいくつかの大きな問題がありました。単一のコンテキストに対して 1 つのクエリを実行する代わりに、異なるコンテキストで複数のクエリを実行する必要がありました。これには実際のパフォーマンス上のペナルティがあります。

最後に、良いニュースは、複数のコンテキストを取り除くのが簡単だったことです。コンテキストは、軽量オブジェクトであることを意図しています。したがって、パフォーマンスが複数のコンテキストの良い議論になるとは思いません。ほとんどの場合、単一のコンテキストの方が単純で、複雑さが少なく、パフォーマンスが向上する可能性が高く、それを機能させるために多くの回避策を実装する必要はないと私は信じています。

複数のコンテキストが役立つ状況を 1 つ考えました。別のコンテキストを使用して、実際に複数のドメインが含まれているデータベースの物理的な問題を修正できます。理想的には、コンテキストはドメインに対して 1 対 1 であり、それはデータベースに対して 1 対 1 です。つまり、テーブルのセットが特定のデータベース内の他のテーブルとまったく関連していない場合、それらはおそらく別のデータベースに引き出されるべきです。これが常に実用的であるとは限らないことを理解しています。しかし、テーブルのセットが非常に異なるため、それらを別々のデータベースに分離しても問題ない (ただし、そうしないことを選択した) 場合は、別々のコンテキストを使用するケースが見られますが、それは実際には 2 つの別々のドメインがあるためです。

マイクロサービスに関しては、単一のコンテキストが依然として理にかなっています。ただし、マイクロサービスの場合、各サービスには、そのサービスに関連するデータベース テーブルのみを含む独自のコンテキストがあります。つまり、サービス x がテーブル 1 と 2 にアクセスし、サービス y がテーブル 3 と 4 にアクセスする場合、各サービスには、そのサービスに固有のテーブルを含む独自のコンテキストがあります。

私はあなたの考えに興味があります。

于 2015-01-15T22:02:40.040 に答える
54

デフォルトのスキーマを設定してコンテキストを区別する

EF6 では、複数のコンテキストを持つことができます。派生クラス (Fluent-API 構成がある場所)のOnModelCreatingメソッドで、既定のデータベース スキーマの名前を指定するだけです。DbContextこれはEF6で機能します:

public partial class CustomerModel : DbContext
{   
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("Customer");

        // Fluent API configuration
    }   
}

この例では、データベース テーブルのプレフィックスとして ("dbo" ではなく) "Customer" を使用します。さらに重要なことに、__MigrationHistoryテーブルの前にプレフィックスも付けられますCustomer.__MigrationHistory__MigrationHistoryしたがって、コンテキストごとに 1 つずつ、1 つのデータベースに複数のテーブルを含めることができます。したがって、1 つのコンテキストに加えた変更が他のコンテキストを台無しにすることはありません。

移行を追加するときは、構成クラスの完全修飾名 (から派生) をコマンドDbMigrationsConfigurationのパラメーターとして指定します。add-migration

add-migration NAME_OF_MIGRATION -ConfigurationTypeName FULLY_QUALIFIED_NAME_OF_CONFIGURATION_CLASS


コンテキストキーについて一言

この MSDN の記事「Chapter - Multiple Models Targeting the Same Database 」によると、テーブルには移行を区別するためのContextKeyMigrationHistory列があるため、テーブルが1 つしか存在しない場合でも、EF 6 はおそらく状況を処理します。

MigrationHistoryただし、上記のようにデフォルトのスキーマを指定して、複数のテーブルを持つことを好みます。


個別の移行フォルダーの使用

このようなシナリオでは、プロジェクト内の別の "Migration" フォルダーを操作することもできます。プロパティDbMigrationsConfigurationを使用して、それに応じて派生クラスを設定できます。MigrationsDirectory

internal sealed class ConfigurationA : DbMigrationsConfiguration<ModelA>
{
    public ConfigurationA()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelA";
    }
}

internal sealed class ConfigurationB : DbMigrationsConfiguration<ModelB>
{
    public ConfigurationB()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelB";
    }
}


概要

全体として、プロジェクト内のコンテキスト、移行フォルダー、データベース内のテーブルなど、すべてが明確に分離されていると言えます。

より大きなトピックの一部であるが、(外部キーを介して) 互いに関連していないエンティティのグループがある場合、私はそのようなソリューションを選択します。

エンティティのグループが互いに何の関係もない場合は、それぞれに個別のデータベースを作成し、おそらく各プロジェクトで 1 つのコンテキストを使用して、異なるプロジェクトでそれらにアクセスします。

于 2015-08-26T13:44:06.880 に答える
9

以下を達成するための簡単な例:

    ApplicationDbContext forumDB = new ApplicationDbContext();
    MonitorDbContext monitor = new MonitorDbContext();

メイン コンテキストでプロパティのスコープを設定するだけです: (DB の作成と維持に使用) 注: 保護されたものを使用するだけです: (エンティティはここでは公開されません)

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("QAForum", throwIfV1Schema: false)
    {

    }
    protected DbSet<Diagnostic> Diagnostics { get; set; }
    public DbSet<Forum> Forums { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Thread> Threads { get; set; }
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

MonitorContext: 別のエンティティをここに公開

public class MonitorDbContext: DbContext
{
    public MonitorDbContext()
        : base("QAForum")
    {

    }
    public DbSet<Diagnostic> Diagnostics { get; set; }
    // add more here
}

診断モデル:

public class Diagnostic
{
    [Key]
    public Guid DiagnosticID { get; set; }
    public string ApplicationName { get; set; }
    public DateTime DiagnosticTime { get; set; }
    public string Data { get; set; }
}

必要に応じて、メインの ApplicationDbContext 内ですべてのエンティティを保護されたものとしてマークし、スキーマの分離ごとに必要に応じて追加のコンテキストを作成できます。

これらはすべて同じ接続文字列を使用しますが、別々の接続を使用するため、トランザクションをクロスせず、ロックの問題に注意してください。一般的に、設計上の分離であるため、とにかくこれは発生しないはずです。

于 2014-10-08T01:30:06.927 に答える
6

注意: 複数のコンテキストを組み合わせる場合は、さまざまなコンテキストのすべての機能をカット アンド ペーストRealContexts.OnModelCreating()して単一のCombinedContext.OnModelCreating().

カスケード削除関係が保持されない理由を探し出すのに時間を無駄にしましたがmodelBuilder.Entity<T>()....WillCascadeOnDelete();、実際のコンテキストから結合されたコンテキストにコードを移植していないことがわかりました。

于 2012-09-10T12:34:31.963 に答える
5

[@JulieLerman の DDD MSDN Mag Article 2013][1] に触発されました。

    public class ShippingContext : BaseContext<ShippingContext>
{
  public DbSet<Shipment> Shipments { get; set; }
  public DbSet<Shipper> Shippers { get; set; }
  public DbSet<OrderShippingDetail> Order { get; set; } //Orders table
  public DbSet<ItemToBeShipped> ItemsToBeShipped { get; set; }
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Ignore<LineItem>();
    modelBuilder.Ignore<Order>();
    modelBuilder.Configurations.Add(new ShippingAddressMap());
  }
}

public class BaseContext<TContext>
  DbContext where TContext : DbContext
{
  static BaseContext()
  {
    Database.SetInitializer<TContext>(null);
  }
  protected BaseContext() : base("DPSalesDatabase")
  {}
}   

「新しい開発を行っていて、Code First にクラスに基づいてデータベースを作成または移行させたい場合は、必要なすべてのクラスと関係を含む DbContext を使用して「uber-model」を作成する必要があります。データベースを表す完全なモデルを構築します。ただし、このコンテキストは BaseContext から継承してはなりません。" JL

于 2016-04-22T09:06:15.510 に答える
4

このデザインに出会ったとき、私の腸は同じことを教えてくれました。

私は、1 つのデータベースに対して 3 つの dbContext があるコード ベースに取り組んでいます。3 つの dbcontext のうち 2 つは、管理データを提供するため、1 つの dbcontext からの情報に依存しています。この設計により、データのクエリ方法に制約が課されました。dbcontexts をまたいで参加できないというこの問題に遭遇しました。代わりに、2 つの個別の dbcontext をクエリしてから、メモリ内で結合するか、両方を反復処理して、2 つの組み合わせを結果セットとして取得する必要があります。これに関する問題は、特定の結果セットを照会する代わりに、すべてのレコードをメモリにロードしてから、メモリ内の 2 つの結果セットに対して結合を行うことです。それは本当に物事を遅くすることができます。

私は質問をします」できるからといって、そうすべきですか?"

この設計に関連して遭遇した問題については、この記事を参照してください。 指定された LINQ 式には、さまざまなコンテキストに関連付けられているクエリへの参照が含まれています。

于 2013-03-13T21:16:10.520 に答える
1

コード ファーストでは、複数の DBContext と 1 つのデータベースのみを持つことができます。コンストラクターで接続文字列を指定するだけです。

public class MovieDBContext : DbContext
{
    public MovieDBContext()
        : base("DefaultConnection")
    {

    }
    public DbSet<Movie> Movies { get; set; }
}
于 2013-12-10T09:27:10.997 に答える