如何使用 EF 使用多源数据库

How to use Multiple source Database using EF

我尝试使用多个 DbContext 将项目的每个域迁移到不同的数据库,如下所示:

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

但看起来我做错了什么所以我得到了每个 DbContext 中的所有表,但我没有任何链接域的 FK。

每个 DbContext 的连接字符串如下:

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))));

我的存储库是这样的:

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)
    {

    }
}

我也这样注册了repo:

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
  }

}

我在我的代码中遗漏了什么来修复它!

任何帮助我该怎么做。

如果我答对了,情况可能是您目前正在处理一个整体并试图将事物分离出来以包围每个业务上下文以进一步将它们分离出来,...可能就像微服务?

我不知道你想要这样做的具体原因,但如果理由不值得,我建议你避免这样做。

我假设你已经阅读了上面的建议,如果我们真的需要这样做,那么首先要了解这些事情:

每个DbContext可以配置自己的实体

假设我们有 3 个 DbContext 的 10 个实体,我们可以将它们分成 ADbContext 的 2 个实体、BDbContext 的 5 个实体和 CDbContext 的 3 个实体。明智地管理它们之间的配置,如 FK,否则,那将变成混乱。以下是如何做到这一点,使用流利的 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...
       });
    }
}

这样,我们只配置和注册与每个DbContext相对应的实体。

我们需要将每个 DbContext 分别注册到任何类型的 DI

你在上面的问题中这样做了,那我就跳过这部分。

为每个 DbContext 组织迁移操作

我通常将它们组织在这样的文件夹中:

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

每个上下文都有自己的迁移和快照。

通过在使用 EF 迁移工具时指定单独的 DbContext 来执行此操作

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

然后通过

分别申请
dotnet ef database update [MigrationName] --context [DbContextName]

就是这样。当然可以,但是我还是建议大家如果没有特殊原因,还是应该避免这种方式。