Programing

엔터티 프레임 워크 : 하나의 데이터베이스, 여러 DbContext.

crosscheck 2020. 5. 14. 22:23
반응형

엔터티 프레임 워크 : 하나의 데이터베이스, 여러 DbContext. 이것은 나쁜 생각입니까?


지금까지 DbContext는 데이터베이스를 나타 내기 때문에 응용 프로그램에서 하나의 데이터베이스를 사용하는 경우 하나의 DbContext 만 필요합니다. 그러나 일부 동료는 기능 영역을 별도의 DbContext 클래스로 분리하려고합니다. 나는 이것이 코드를 깨끗하게 유지하려는 욕구에서 좋은 곳에서 온다고 생각하지만 변동성이있는 것 같습니다. 내 직감은 나에게 나쁜 생각이라고 말하지만 불행히도 내 직감은 디자인 결정에 충분한 조건이 아닙니다.

그래서 나는 이것이 왜 나쁜 생각인지에 대한 구체적인 예를 찾고 있습니다. 또는 B) 이것이 모두 잘 작동 할 것이라고 확신합니다.


단일 데이터베이스에 대해 여러 컨텍스트를 가질 수 있습니다. 예를 들어 데이터베이스에 여러 데이터베이스 스키마가 포함되어 있고 각 스키마를 별도의 자체 포함 영역으로 처리하려는 경우 유용 할 수 있습니다.

문제는 코드를 먼저 사용하여 데이터베이스를 만들 때입니다. 응용 프로그램의 단일 컨텍스트 만 가능합니다. 이를위한 트릭은 일반적으로 데이터베이스 작성에만 사용되는 모든 엔티티를 포함하는 하나의 추가 컨텍스트입니다. 엔터티의 하위 집합 만 포함하는 실제 응용 프로그램 컨텍스트에는 데이터베이스 이니셜 라이저가 null로 설정되어 있어야합니다.

공유 된 엔터티 유형 및 하나의 컨텍스트에서 다른 컨텍스트로의 전달 등과 같이 여러 컨텍스트 유형을 사용할 때 표시되는 다른 문제가 있습니다. 일반적으로 가능하면 디자인을 훨씬 깔끔하게 만들고 다른 기능 영역을 분리 할 수 ​​있지만 추가 복잡성의 비용.


이 스레드는 방금 StackOverflow에서 버블 링되었으므로 다른 "B) 이것이 모두 괜찮을 것이라는 확신을 제공하고 싶었습니다. :)

DDD Bounded Context 패턴을 사용하여 정확하게이 작업을 수행하고 있습니다. 필자는이 책에 대해 Programming Entity Framework : DbContext에 썼으며 Pluralsight-> http://pluralsight.com/training/Courses/TableOfContents/efarchitecture 과정 중 하나에서 50 분 모듈에 중점을 둡니다 .


나는 약 4 년 전에이 답변을 썼는데 제 의견은 변하지 않았습니다. 그러나 그 이후로 마이크로 서비스 분야에서 상당한 발전이있었습니다. 마지막에 마이크로 서비스 관련 메모를 추가했습니다 ...

표를 뒷받침 할 실제 경험을 바탕으로 아이디어에 반대합니다.

단일 데이터베이스에 대한 5 개의 컨텍스트가있는 큰 응용 프로그램으로 전환되었습니다. 결국 우리는 하나의 컨텍스트로 되 돌리는 것을 제외하고 모든 컨텍스트를 제거했습니다.

처음에는 여러 상황에 대한 아이디어가 좋은 아이디어처럼 보입니다. 데이터 액세스를 도메인으로 분리하고 몇 가지 깔끔하고 가벼운 컨텍스트를 제공 할 수 있습니다. DDD처럼 들리 죠? 이는 데이터 액세스를 단순화합니다. 또 다른 주장은 필요한 컨텍스트에만 액세스한다는 점에서 성능에 대한 것입니다.

그러나 실제로 응용 프로그램이 커짐에 따라 많은 테이블이 다양한 컨텍스트에서 관계를 공유했습니다. 예를 들어, 컨텍스트 1에서 테이블 A에 대한 쿼리는 컨텍스트 2에서 테이블 B를 조인해야했습니다.

이것은 우리에게 몇 가지 잘못된 선택을 남겼습니다. 다양한 상황에서 테이블을 복제 할 수 있습니다. 우리는 이것을 시도했다. 이로 인해 각 엔티티에 고유 한 이름이 있어야하는 EF 제한 조건을 포함한 여러 맵핑 문제점이 발생했습니다. 그래서 우리는 다른 상황에서 Person1과 Person2라는 엔티티로 끝났습니다. 우리는 이것이 저조한 디자인이라고 주장 할 수 있지만 최선의 노력에도 불구하고 이것이 실제로 응용 프로그램이 실제 세계에서 어떻게 성장했는지입니다.

또한 필요한 데이터를 얻기 위해 두 컨텍스트를 모두 쿼리했습니다. 예를 들어, 우리의 비즈니스 로직은 컨텍스트 1에서 필요한 것의 절반을 컨텍스트 2에서 쿼리합니다.이 문제에는 몇 가지 중요한 문제가있었습니다. 단일 컨텍스트에 대해 하나의 쿼리를 수행하는 대신 서로 다른 컨텍스트에서 여러 쿼리를 수행해야했습니다. 이것은 실제 성능 저하가 있습니다.

결국 좋은 소식은 여러 컨텍스트를 쉽게 제거 할 수 있다는 것입니다. 문맥은 가벼운 물체로 만들어졌습니다. 따라서 여러 상황에서 성능이 좋은 주장이라고 생각하지 않습니다. 거의 모든 경우에있어, 단일 컨텍스트는 더 단순하고 덜 복잡하며 성능이 더 우수 할 것으로 생각하며 작동하기 위해 여러 가지 해결 방법을 구현할 필요가 없습니다.

여러 상황이 유용 할 수있는 상황을 생각했습니다. 실제로 하나 이상의 도메인을 포함하는 데이터베이스의 물리적 문제를 해결하기 위해 별도의 컨텍스트를 사용할 수 있습니다. 이상적으로 컨텍스트는 도메인과 일대일이며 데이터베이스와 일대일입니다. 즉, 테이블 세트가 주어진 데이터베이스의 다른 테이블과 관련이없는 경우 별도의 데이터베이스로 가져와야합니다. 이것이 항상 실용적이지는 않다는 것을 알고 있습니다. 그러나 테이블 세트가 너무 다르기 때문에 테이블을 별도의 데이터베이스로 분리하는 것이 편하다고 생각하지만 (그렇지 않은 경우) 실제로는 별도의 도메인이 두 개 있기 때문에 별도의 컨텍스트를 사용하는 경우를 볼 수 있습니다.

마이크로 서비스와 관련하여 하나의 단일 컨텍스트가 여전히 의미가 있습니다. 그러나 마이크로 서비스의 경우 각 서비스에는 해당 서비스와 관련된 데이터베이스 테이블 만 포함하는 고유 한 컨텍스트가 있습니다. 다시 말해, 서비스 x가 테이블 1 및 2에 액세스하고 서비스 y가 테이블 3 및 4에 액세스하는 경우, 각 서비스는 해당 서비스에 특정한 테이블을 포함하는 고유 한 컨텍스트를 갖습니다.

나는 당신의 생각에 관심이 있습니다.


기본 스키마를 설정하여 컨텍스트 구별

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단일 데이터베이스에 각 컨텍스트마다 하나씩 둘 이상의 테이블 이있을 수 있습니다 . 따라서 한 컨텍스트에서 변경 한 내용은 다른 컨텍스트와 혼동되지 않습니다.

마이그레이션을 추가 할 때 명령 DbMigrationsConfiguration에서 매개 변수로 구성 클래스의 완전한 이름 (에서 파생 됨 )을 지정하십시오 add-migration.

add-migration NAME_OF_MIGRATION -ConfigurationTypeName FULLY_QUALIFIED_NAME_OF_CONFIGURATION_CLASS


컨텍스트 키에 대한 짧은 단어

According to this MSDN article "Chapter - Multiple Models Targeting the Same Database" EF 6 would probably handle the situation even if only one MigrationHistory table existed, because in the table there is a ContextKey column to distinguish the migrations.

However I prefer having more than one MigrationHistory table by specifying the default schema like explained above.


Using separate migration folders

In such a scenario you might also want to work with different "Migration" folders in you project. You can set up your DbMigrationsConfiguration derived class accordingly using the MigrationsDirectory property:

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";
    }
}


Summary

All in all, you can say that everything is cleanly separated: Contexts, Migration folders in the project and tables in the database.

I would choose such a solution, if there are groups of entities which are part of a bigger topic, but are not related (via foreign keys) to one another.

If the groups of entities do not have anything to do which each other, I would created a separate database for each of them and also access them in different projects, probably with one single context in each project.


Simple example to achieve the below:

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

Just scope the properties in the main context: (used to create and maintain the DB) Note: Just use protected: (Entity is not exposed here)

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: Expose separate Entity here

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

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

Diagnostics Model:

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

If you like you could mark all entities as protected inside the main ApplicationDbContext, then create additional contexts as needed for each separation of schemas.

They all use the same connection string, however they use separate connections, so do not cross transactions and be aware of locking issues. Generally your designing separation so this shouldn't happen anyway.


Reminder: If you do combine multiple contexts make sure you cut n paste all the functionality in your various RealContexts.OnModelCreating() into your single CombinedContext.OnModelCreating().

I just wasted time hunting down why my cascade delete relationships weren't being preserved only to discover that I hadn't ported the modelBuilder.Entity<T>()....WillCascadeOnDelete(); code from my real context into my combined context.


My gut told me the same thing when I came across this design.

I am working on a code base where there are three dbContexts to one database. 2 out of the 3 dbcontexts are dependent on information from 1 dbcontext because it serves up the administrative data. This design has placed constraints on how you can query your data. I ran into this problem where you cannot join across dbcontexts. Instead what you are required to do is query the two separate dbcontexts then do a join in memory or iterate through both to get the combination of the two as a result set. The problem with that is instead of querying for a specific result set you are now loading all your records into memory and then doing a join against the two result sets in memory. It can really slow things down.

I would ask the question "just because you can, should you?"

See this article for the problem I came across related to this design. The specified LINQ expression contains references to queries that are associated with different contexts


In code first, you can have multiple DBContext and just one database. You just have to specify the connection string in the constructor.

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

    }
    public DbSet<Movie> Movies { get; set; }
}

Another bit of "wisdom". I have a database facing both, the internet and an internal app. I have a context for each face. That helps me to keep a disciplined, secured segregation.


Inspired by [@JulieLerman 's 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")
  {}
}   

"If you’re doing new development and you want to let Code First create or migrate your database based on your classes, you’ll need to create an “uber-model” using a DbContext that includes all of the classes and relationships needed to build a complete model that represents the database. However, this context must not inherit from BaseContext." JL


I want to share a case, where I think the possibility of having multiple DBContexts in the same database makes good sense.

I have a solution with two database. One is for domain data except user information. The other is solely for user information. This division is primarily driven by the EU General Data Protection Regulation. By having two databases, I can freely move the domain data around (e.g. from Azure to my development environment) as long as the user data stays in one secure place.

Now for the user database I have implemented two schemas through EF. One is the default one provided by the AspNet Identity framework. The other is our own implementing anything else user related. I prefer this solution over extending the ApsNet schema, because I can easily handle future changes to AspNet Identity and at the same time the separation makes it clear to the programmers, that "our own user information" goes in the specific user schema we have defined.

참고URL : https://stackoverflow.com/questions/11197754/entity-framework-one-database-multiple-dbcontexts-is-this-a-bad-idea

반응형