Entity Framework Core 与多个数据库上下文、模式和项目





5.00/5 (5投票s)
当数据库模式被分割成多个项目和数据库上下文时,如何使用 EF Core 代码优先方法。
引言
有时数据库模式会变得复杂和庞大,在这种情况下,数据库模式通常被分成多个模式,其中一个团队可以负责数据库模式和访问模式的后端代码的子集。Entity Framework Core (v2.0) 允许将数据库模式分离成多个项目,其中一个项目包含一个或多个数据库上下文。
示例解决方案将使用以下两个模式中的表
- Users.User
- Users.Role
- Products.Product
MVC Web API 项目
第一步是创建一个 MVC Web API Core 项目,该项目稍后将使用我们的数据库上下文。我将入口项目和解决方案命名为 MultipleDbContexts。需要引用一些 nuget 包。引用所有包后,MultipleDbContexts.csproj 文件如下所示
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.2" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.2" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" 
     Version="2.0.0-preview1-final" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.3" />
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.2" />
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.2" />
  </ItemGroup>
</Project>
ValuesController 由模板添加,我们暂时保留它。Startup.cs 也一样,稍后我们会更改它。
现在我们需要一个新的数据库,为此,我们可以使用免费的 SQL Server Express 或 (也免费) SQL Server for developers。创建一个新的空数据库,并将其命名为 multipleDbContexts。
为了使用数据库,我们需要将连接字符串添加到 appsettings.json 文件中,它应该如下所示
{
  "connectionString": "Server=host\\instance;Database=multipleDbContexts;Trusted_Connection=True;",
  "Logging": {
    // ...
  }
}
数据库上下文项目
MultipleDbContexts.Users 项目将有一个 UsersDbContext,它将引用表 Users.User 和 Users.Role。
将新的类库添加到解决方案中,并在入口项目中引用它。MultipleDbContexts.Users 类库需要引用 EF nugets,所以在添加所有必需的包后,MultipleDbContexts.Users.csproj 如下所示
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.2" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.2" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" 
                      Version="2.0.0-preview1-final" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference 
       Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.3" />
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.2" />
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.2" />
  </ItemGroup>
</Project>
现在我们需要创建 MultipleDbContexts.Products 类库,并引用相同的 nuget 包,并将入口项目中的引用设置为 MultipleDbContexts.Products。
是时候将表模型添加到 MultipleDbContexts.Products 和 MultipleDbContexts.Users 了。
MultipleDbContexts.Products 有以下文件
Products.cs
[Table("Product", Schema = "Products")]
public class Products
{
   public int Id { get; set; }
   [MaxLength(60)]
   public string Name { get; set; }
   [MaxLength(600)]
   public string ImageUrl { get; set; }
}
ProductsDbContext.cs
public class ProductsDbContext : DbContext
{
    private readonly IConfiguration _config;
    public ProductsDbContext(IConfiguration config, DbContextOptions<ProductsDbContext> options)
        : base (options)
    {
        _config = config ?? throw new System.ArgumentNullException(nameof(config));
    }
    public DbSet<Products> Products { get; set; }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(_config["connectionString"], options =>
        {
            options.MigrationsHistoryTable("__UsersMigrationsHistory", "Products");
        });
    }
}
您可以从上面的文件中注意到一些事情
- Products类有一个- TableAttribute,它定义了表的名称和模式的名称。
- ProductsDbContext构造函数接受- IConfiguration,这对于连接字符串是必需的。
- ProductsDbContext构造函数的第二个参数是- DbContextOptions<ProductsDbContext>,在使用多个数据库上下文时,我们需要指定泛型- DbContextOptions<>参数,而不是使用- DbContextOptions。
- OnConfiguring方法设置要使用的迁移历史记录表,这是因为- 我们希望迁移历史记录表与表位于同一模式中,并且
- 我们希望为每个数据库上下文拥有单独的迁移
 
现在,我们需要将以下文件添加到 MultipleDbContexts.Users 项目中
RoleModel.cs
[Table("Role", Schema = "Users")]
public class RoleModel
{
    [Key, MaxLength(60)]
    public string Key { get; set; }
    [Required, MaxLength(250)]
    public string Description { get; set; }
}
UserModel.cs
[Table("User", Schema = "Users")]
public class UserModel
{
    [Key]
    public int Id { get; set; }
    [Required, MaxLength(50)]
    public string Email { get; set; }
    
    [StringLength(500)]
    public string AboutUser { get; set; }
}
UsersDbContext.cs
public class UsersDbContext : DbContext
{
    private readonly IConfiguration _config;
    public UsersDbContext(IConfiguration config, DbContextOptions<UsersDbContext> options)
        : base(options)
    {
        _config = config ?? throw new System.ArgumentNullException(nameof(config));
    }
    public DbSet<UserModel> Users { get; set; }
    public DbSet<RoleModel> Roles { get; set; }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(_config["connectionString"], options =>
        {
            options.MigrationsHistoryTable("__UsersMigrationsHistory", "Users");
        });
    }
}
以下屏幕截图显示了解决方案结构。

现在我们需要将我们的数据库上下文添加到 Startup.cs 中的 DI 容器中
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<UsersDbContext>(options =>
    {
        options.UseSqlServer(Configuration["connectionString"],
            sqlServerOptions =>
            {
                sqlServerOptions.MigrationsAssembly("MultipleDbContexts.Users");
            });
    });
    services.AddDbContext<ProductsDbContext>(options =>
    {
        options.UseSqlServer(Configuration["connectionString"],
            sqlServerOptions =>
            {
                sqlServerOptions.MigrationsAssembly("MultipleDbContexts.Products");
            });
    });
    services.AddMvc();
}
Migrations
为了使迁移起作用,在使用 dotnet ef 命令时,我们需要指定入口项目。这是通过 -s 参数完成的。我们还需要指定要应用迁移的上下文,这通过 --context 参数完成。
在 MultipleDbContexts.Products 所在的目录中打开命令提示符,然后输入以下命令
dotnet ef migrations add products -s ../MultipleDbContexts/ --context ProductsDbContext
dotnet ef database update -s ../MultipleDbContexts/ --context ProductsDbContext
MultipleDbContexts.Users 项目目录中也需要类似的命令。
dotnet ef migrations add users -s ../MultipleDbContexts/ --context UsersDbContext
dotnet ef database update -s ../MultipleDbContexts/ --context UsersDbContext
现在我们的数据库应该有表了。让我们添加一些测试数据,在 multipleDbContexts 数据库上执行以下 SQL
INSERT INTO [Users].[User] (AboutUser, Email) VALUES ('About user', 'me@example.com')
INSERT INTO [Users].[Role] ([Key], [Description]) VALUES ('Admin', 'Super Admin')
INSERT INTO Products.Product (ImageUrl, Name) VALUES ('/products/teapot.png', 'Teapot')
确保一切正常
一切都应该就绪,编辑 ValuesController 以使用数据库上下文
[Route("api/[controller]")]
public class ValuesController : Controller
{
    // GET api/values
    [HttpGet]
    public object Get(
        [FromServices] UsersDbContext usersDb, 
        [FromServices] ProductsDbContext productsDb
        )
    {
        return new
        {
            usersDb.Users,
            usersDb.Roles,
            productsDb.Products
        };
    }
}
在 Visual Studio 中点击 F5 应该会打开浏览器,并且导航到 /api/values 应该会返回以下 JSON
{
   "users":[
      {
         "id":1,
         "email":"me@example.com",
         "aboutUser":"About user"
      }
   ],
   "roles":[
      {
         "key":"Admin",
         "description":"Super Admin"
      }
   ],
   "products":[
      {
         "id":1,
         "name":"Teapot",
         "imageUrl":"/products/teapot.png"
      }
   ]
}
最终注释
对于那些走到这一步的人,有几点说明
- 我还没有用其他的 EF Core 提供程序测试过这个,但是它应该可以工作。
- 我知道这个例子非常简单,我们甚至没有外键,但这不是本文的重点。
- 如果您有任何问题,可以在评论中找到我。


