在Entity Framework Core中動態更改架構

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

UPD 這裡是我解決問題的方式。雖然它可能不是最好的,但它對我有用。


我在使用EF Core時遇到問題。我想通過模式機制在項目數據庫中為不同公司分離數據。我的問題是如何在運行時更改模式名稱?我已經找到了關於這個問題的類似問題 ,但它仍然是答案,我有一些不同的條件。所以我有Resolve方法,在必要時授予db-context

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

我可以在OnModelCreating設置schema-name,但是,如前所述,這個方法只調用一次,所以我可以在這裡設置模式名稱globaly

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

或者通過類似的屬性在模型中

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

但是如何在運行時更改模式名稱?我根據每個請求創建了ef的上下文,但首先我通過請求為數據庫中的某個模式共享表格查找了用戶的模式名稱。那麼組織該機制的真正方法是什麼:

  1. 根據用戶的憑據計算出模式名稱;
  2. 從特定架構的數據庫中獲取特定於用戶的數據。

謝謝。

PS我使用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 = "Production";
}

要創建具有特定模式的DbContext,您可以這樣寫:

var humanResourcesDbContext = new AdventureWorksDbContext(Schemas.HumanResources);

var productionDbContext = new AdventureWorksDbContext(Schemas.Production);

顯然你應該根據schema的name參數值設置模式名稱:

entity.ToTable("Product", schemaName);

熱門答案

有幾種方法可以做到這一點:

  • 在外部構建模型並通過DbContextOptionsBuilder.UseModel()傳遞它
  • IModelCacheKeyFactory服務替換為考慮模式的服務


Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因