65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2018年3月30日

CPOL

3分钟阅读

viewsIcon

67450

downloadIcon

718

当数据库模式被分割成多个项目和数据库上下文时,如何使用 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.UserUsers.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.ProductsMultipleDbContexts.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 方法设置要使用的迁移历史记录表,这是因为
    1. 我们希望迁移历史记录表与表位于同一模式中,并且
    2. 我们希望为每个数据库上下文拥有单独的迁移

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