# 9.11、多上下文、读写分离

## 多上下文

默认情况下，Hoa Framework 只有一个默认的数据库上下文：`Hoa.EntityFramewok.Core.HoaDbContext`，也就是默认只能操作一个数据库。

在 Hoa Framework **1.2.0** 版本中，新增了多数据库上下文的支持。通过配置数据库多上下文可以同时操作多个数据库、并将数据库包裹在同一个事务中。

## 多上下文配置使用

### 第一步

在 `appsetting.json` 中新增新的数据库连接字符串。

```javascript
{
  "ConnectionStrings": {
    "HoaDatabase": "Server=localhost;Database=Hoa;User=sa;Password=000000;",
    "OtherDatabase": "Server=localhost;Database=Hoa;User=sa;Password=000000;"
  }
}
```

### 第二步

在 `Hoa.EntityFramework.Core` 层创建新的 `DbContext`，如 `OtherDbContext`：

```csharp
using Microsoft.EntityFrameworkCore;

namespace Hoa.EntityFrameworkCore
{
    public partial class OtherDbContext : DbContext
    {
        public OtherDbContext(DbContextOptions<OtherDbContext> options)
            : base(options)
        {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer("Name=OtherDatabase");
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            OnModelCreatingPartial(modelBuilder);
        }

        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
    }
}
```

### 第三步

在 `Hoa.Web.Core.ServiceExtensions.HoaDbContextConfigureExtension` 中配置新的 `DbContext` 信息：

```csharp
using Hoa.EntityFrameworkCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Hoa.Web.Core.ServiceExtensions
{
    public static class HoaDbContextConfigureExtension
    {
        public static IServiceCollection AddHoaDbContext(this IServiceCollection services, IWebHostEnvironment env)
        {
            services.AddDbContextPool<HoaDbContext>(options =>
            {
                options.UseSqlServer(AppGlobal.Configuration.GetConnectionString("HoaDatabase"));
                if (env.IsDevelopment())
                {
                    options/*.UseLazyLoadingProxies()*/
                                .EnableDetailedErrors()
                                .ConfigureWarnings(c => c.Log((RelationalEventId.CommandExecuting, LogLevel.Debug)));
                }
            }
            , poolSize: 128);
            
            // 配置 OtherDbContext
            services.AddDbContextPool<OtherDbContext>(options =>
            {
                options.UseSqlServer(AppGlobal.Configuration.GetConnectionString("OtherDatabase"));
                if (env.IsDevelopment())
                {
                    options/*.UseLazyLoadingProxies()*/
                                .EnableDetailedErrors()
                                .ConfigureWarnings(c => c.Log((RelationalEventId.CommandExecuting, LogLevel.Debug)));
                }
            }
            , poolSize: 128);

            return services;
        }
    }
}
```

### 第四步

在 `Hoa.Core` 层创建 `OtherDbContextIdentifier` 数据库上下文标识类，**注意：必须以 `Identifier` 结尾！！！并继承 `DbContextIdentifier` 类。**

```csharp
using Hoa.DbManager.Identifiers;

namespace Hoa.Core
{
    public class OtherDbContextIdentifier : DbContextIdentifier
    {
        public OtherDbContextIdentifier() : base(nameof(OtherDbContextIdentifier))
        {
        }
    }
}

```

### 第五步

在 `Hoa.EntityFrameworkCore.HoaEntityFrameworkCoreModule` 中新增配置代码：

```csharp
using Autofac;
using Hoa.DbManager.Identifiers;
using Microsoft.EntityFrameworkCore;

namespace Hoa.EntityFrameworkCore
{
    public class HoaEntityFrameworkCoreModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<HoaDbContext>()
                .As<DbContext>()
                .InstancePerLifetimeScope();

            builder.RegisterType<HoaDbContext>()
                .Named<DbContext>(nameof(HoaDbContextIdentifier))
                .InstancePerLifetimeScope();
            
            // 配置 OtherDbContext
            builder.RegisterType<OtherDbContext>()
                .Named<DbContext>(nameof(OtherDbContextIdentifier))
                .InstancePerLifetimeScope();
        }
    }
}
```

### 第六步

上述配置好后，可以通过 `IDynamicRepository<TEntity,TDbContextIdentifier>` 初始化了：

```csharp
using Hoa.Core.Test.Entities;
using Hoa.Dependencies;
using Hoa.ServiceController.Attributes;
using Hoa.UnitOfWork.Repositories;
using System.ComponentModel.DataAnnotations;

namespace Hoa.Application.Test
{
    [HoaServiceController]
    public class TestAppService : ITestAppService, IAppServiceDependency
    {
        private readonly IRepository<TestEntity> _testRepository;
        
        // 初始化 OtherDbContext
        private readonly IDynamicRepository<TestEntity,OtherDbContextIdentifier> _testOtherRepository;
        
        public TestAppService(
            IRepository<TestEntity> testRepository
            // 在构造函数中注入
            , IDynamicRepository<TestEntity,OtherDbContextIdentifier> testOtherRepository;
            )
        {
            _testRepository = testRepository;
            
            // 使用
            _testOtherRepository = testOtherRepository;
        }

        // ... Other Codes
    }
}
```

关键代码

```csharp
private readonly IDynamicRepository<TestEntity,OtherDbContextIdentifier> _testOtherRepository;
```

## 读写分离配置

文档整理中......

## 关于分布式事务

在多数据库操作上下文操作数据库中，默认开启了分布式事务（同一个服务器内的不同数据库），如果数据库操作上下文不在同一个服务器上将报错：

```
System.PlatformNotSupportedException: This platform does not support distributed transactions.
```

那是因为 .NET Core 3.x 还[不支持跨平台的分布式事务](https://docs.microsoft.com/zh-cn/ef/core/saving/transactions#using-systemtransactions)，需要在 .NET 5.x 版本才支持。

目前提供的解决方案是：在方法上面关闭分布式事务即可。

```csharp
[UnitOfWork(false)]
```
