c# - Entity Framework Core 中的动态更改架构

标签 c# entity-framework asp.net-core database-schema entity-framework-core

UPD here是我解决问题的方法。虽然它可能不是最好的,但它对我有用。


我在使用 EF Core 时遇到问题。我想通过模式机制在我的项目数据库中分离不同公司的数据。我的问题是如何在运行时更改架构名称?我找到了 similar question关于这个问题,但仍然没有答案,我有一些不同的条件。所以我有 Resolve 方法在必要时授予 db-context

public static void Resolve(IServiceCollection services) {
    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<DomainDbContext>()
        .AddDefaultTokenProviders();
    services.AddTransient<IOrderProvider, OrderProvider>();
    ...
}

我可以在 OnModelCreating 中设置模式名称,但是,正如之前发现的那样,这个方法只被调用一次,所以我可以像那样全局设置模式名称

protected override void OnModelCreating(ModelBuilder modelBuilder) {
    modelBuilder.HasDefaultSchema("public");
    base.OnModelCreating(modelBuilder);
}

或通过属性直接在模型中

[Table("order", Schema = "public")]
public class Order{...}

但是我怎样才能在运行时更改架构名称呢?我为每个请求创建上下文,但首先我通过对数据库中模式共享表的请求来确定用户的模式名称。那么组织该机制的正确方法是什么:

  1. 根据用户凭据找出架构名称;
  2. 从特定模式的数据库中获取特定于用户的数据。

谢谢。

附言我使用 PostgreSql,这就是小写表名的原因。

最佳答案

您是否已在 EF6 中使用 EntityTypeConfiguration?

我认为解决方案是在 DbContext 类的 OnModelCreating 方法上使用实体映射,如下所示:

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal;
using Microsoft.Extensions.Options;

namespace AdventureWorksAPI.Models
{
    public class AdventureWorksDbContext : Microsoft.EntityFrameworkCore.DbContext
    {
        public AdventureWorksDbContext(IOptions<AppSettings> appSettings)
        {
            ConnectionString = appSettings.Value.ConnectionString;
        }

        public String ConnectionString { get; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(ConnectionString);

            // this block forces map method invoke for each instance
            var builder = new ModelBuilder(new CoreConventionSetBuilder().CreateConventionSet());

            OnModelCreating(builder);

            optionsBuilder.UseModel(builder.Model);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.MapProduct();

            base.OnModelCreating(modelBuilder);
        }
    }
}

OnConfiguring 方法上的代码强制在为 DbContext 类创建每个实例时执行 MapProduct。

MapProduct方法的定义:

using System;
using Microsoft.EntityFrameworkCore;

namespace AdventureWorksAPI.Models
{
    public static class ProductMap
    {
        public static ModelBuilder MapProduct(this ModelBuilder modelBuilder, String schema)
        {
            var entity = modelBuilder.Entity<Product>();

            entity.ToTable("Product", schema);

            entity.HasKey(p => new { p.ProductID });

            entity.Property(p => p.ProductID).UseSqlServerIdentityColumn();

            return modelBuilder;
        }
    }
}

正如您在上面看到的,有一行设置表的模式和名称,您可以在 DbContext 或类似的东西中为一个构造函数发送模式名称。

请不要使用魔法字符串,您可以创建一个包含所有可用模式的类,例如:

using System;

public class Schemas
{
    public const String HumanResources = "HumanResources";
    public const String Production = "Production";
    public const String Sales = "Sales";
}

要创建具有特定模式的 DbContext,您可以这样写:

var humanResourcesDbContext = new AdventureWorksDbContext(Schemas.HumanResources);

var productionDbContext = new AdventureWorksDbContext(Schemas.Production);

显然你应该根据模式名称参数的值设置模式名称:

entity.ToTable("Product", schemaName);

关于c# - Entity Framework Core 中的动态更改架构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39499470/

相关文章:

c# - 将 UTC 日期时间转换为另一个时区

c# - 同一个表的多个外键具有级联更新和删除

c# - C# 中的 IHostedService 不会每次都触发方法吗?

c# - 在第一次加载之前通过授权服务器保护 SPA

asp.net-mvc - Entity Framework 代码优先迁移 NOCHECK 约束?

c# - 如何捕获 EF 异常

c# - Moq:如何获取传递给模拟服务方法的参数

c# - 反序列化

c# - 将 Pervasive 导出到 MySQL (C#)

c# - 无法为可组合函数创建函数导入