Home > Software design >  How to use Multiple source Database using EF
How to use Multiple source Database using EF

Time:11-28

I tried to use Multiple DbContext to migrate each domain of my project to deferent database like this:

public class AppDbContext: DbContext
.
.
public class UserAccessDbContext: DbContext
.
.
public class AdministrationDbContext: DbContext
.
.
Etc

but it's look like I make something wrong so I get all Tables in each DbContext, I don't have any FK that linking the domains.

My connection string for each DbContext like this:

public static void AppDbContext(this IServiceCollection services, string connectionString) =>
   services.AddDbContext<AppDbContext>(options => options.UseMySql(connectionString, new MySqlServerVersion(new Version(8, 0, 27))));

  public static void UserAccessDbContext(this IServiceCollection services, string connectionString) =>
   services.AddDbContext<UserAccessDbContext>(options => options.UseMySql(connectionString, new MySqlServerVersion(new Version(8, 0, 27))));
  
  public static void AdministrationDbContext(this IServiceCollection services, string connectionString) =>
   services.AddDbContext<AdministrationDbContext>(options => options.UseMySql(connectionString, new MySqlServerVersion(new Version(8, 0, 27))));

And my Repository like this:

public class AppRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot
{
  public AppRepository(AppDbContext appDbContext) : base(appDbContext)
  {
  }
}
.
.
.
public class AdministrationRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot
{
    public AdministrationRepository(AdministrationDbContext administrationdbContext) : base(administrationdbContext)
    {
    }
}
.
.
.
public class UserAccessRepository<T> : RepositoryBase<T>, IReadRepository<T>, IRepository<T> where T : class, IAggregateRoot
{
    public UserAccessRepository(UserAccessDbContext userAccessdbContext) : base(userAccessdbContext)
    {

    }
}

I also registered the repo like this:

public class DefaultInfrastructureModule : Module
{
  private readonly bool _isDevelopment = false;
  private readonly List<Assembly> _assemblies = new List<Assembly>();

  public DefaultInfrastructureModule(bool isDevelopment, Assembly? callingAssembly = null)
  {
    _isDevelopment = isDevelopment;
    var coreAssembly = Assembly.GetAssembly(typeof(User)); // TODO: Replace "Project" with any type from your Core project
    var infrastructureAssembly = Assembly.GetAssembly(typeof(StartupSetup));
    if (coreAssembly != null)
    {
      _assemblies.Add(coreAssembly);
    }
    if (infrastructureAssembly != null)
    {
      _assemblies.Add(infrastructureAssembly);
    }
    if (callingAssembly != null)
    {
      _assemblies.Add(callingAssembly);
    }
  }

  protected override void Load(ContainerBuilder builder)
  {
    if (_isDevelopment)
    {
      RegisterDevelopmentOnlyDependencies(builder);
    }
    else
    {
      RegisterProductionOnlyDependencies(builder);
    }
    RegisterCommonDependencies(builder);
  }

  private void RegisterCommonDependencies(ContainerBuilder builder)
  {
    //Start Repository Reg
    builder.RegisterGeneric(typeof(AppRepository<>))
        .As(typeof(IRepository<>))
        .As(typeof(IReadRepository<>))
        .InstancePerLifetimeScope();

    builder.RegisterGeneric(typeof(AdministrationRepository<>))
        .As(typeof(IRepository<>))
        .As(typeof(IReadRepository<>))
        .InstancePerLifetimeScope();

    builder.RegisterGeneric(typeof(UserAccessRepository<>))
        .As(typeof(IRepository<>))
        .As(typeof(IReadRepository<>))
        .InstancePerLifetimeScope();
    //End Repository Reg

    builder
        .RegisterType<Mediator>()
        .As<IMediator>()
        .InstancePerLifetimeScope();

    builder.Register<ServiceFactory>(context =>
    {
      var c = context.Resolve<IComponentContext>();
      return t => c.Resolve(t);
    });

    var mediatrOpenTypes = new[]
    {
      typeof(IRequestHandler<,>),
      typeof(IRequestExceptionHandler<,,>),
      typeof(IRequestExceptionAction<,>),
      typeof(INotificationHandler<>),
    };

    foreach (var mediatrOpenType in mediatrOpenTypes)
    {
      builder
      .RegisterAssemblyTypes(_assemblies.ToArray())
      .AsClosedTypesOf(mediatrOpenType)
      .AsImplementedInterfaces();
    }

    builder.RegisterType<EmailSender>().As<IEmailSender>()
      .InstancePerLifetimeScope();
  }

  private void RegisterDevelopmentOnlyDependencies(ContainerBuilder builder)
  {
    // TODO: Add development only services
  }

  private void RegisterProductionOnlyDependencies(ContainerBuilder builder)
  {
    // TODO: Add production only services
  }

}

What I miss in my code to fix it !

Any help how can I do it.

CodePudding user response:

If I get the question right, the case might be you're currently working on a monolith and trying to separate things out to surround each business context for further separate them out,... might be like microservices ?

I don't know about the specific reason why you would want to do that, but if the reason doesn't worth it, I suggest you avoid to doing that.

I assume you read above recommendation, and if it's the case we really need to do that, then understand these things first:

Each DbContext can config their own entity

Let assume we have 10 entities for 3 DbContext, we can separate them out 2 entities for ADbContext, 5 for BDbContext and 3 for CDbContext. Manage the configuration like FK, between them wisely, otherwise, that would turn to be chaos. Here is how to doing this, using fluent API

public class MyAppDbContext : DbContext
{
    public DbSet<OurEntity> OurEntity { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<OurEntity>(builder => 
       {
            // Our implementation goes here...
       });
    }
}

Like that, we config and register only entities correspondingly with each DbContext.

We need to register each DbContext separately to whatever kind of DI

You does this above in the question, then I'll skip this part.

Organize Migration operation for each DbContext

I usually organize them in folder like this:

  • Data/Migrations/ContextA
  • Data/Migrations/ContextB
  • Data/Migrations/ContextC

Each context have their own Migrations and snapshot.

Doing this by specify separate DbContext when using EF migration tool

dotnet ef migrations add [MigrationName] --context [DbContextName] --output-dir [Path to desire place to store migration]

then apply them separately by

dotnet ef database update [MigrationName] --context [DbContextName]

That's it. Of course we can do it, but I still suggest we should avoid this approach if there was no specific reason.

  • Related