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 提供程序测试过这个,但是它应该可以工作。
- 我知道这个例子非常简单,我们甚至没有外键,但这不是本文的重点。
- 如果您有任何问题,可以在评论中找到我。