Entity Framework Core中动态更改架构

45

UPD 这里 是我解决问题的方法。虽然可能不是最好的方法,但对我有效。


我在使用EF Core时遇到了一个问题。我想通过模式机制来将不同公司的数据分开存储在项目的数据库中。我的问题是如何在运行时更改模式名称?我找到了类似的问题,但它仍未得到解答,而且我有一些不同的条件。因此,我有一个Resolve方法,用于在必要时授予db-context。

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

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

我可以在OnModelCreating中设定架构名称,但正如之前所发现的,该方法只会被调用一次,所以我可以像这样全局设定架构名称。

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

或者直接通过属性在模型中进行修改

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

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

  1. 通过用户凭据确定模式名称;
  2. 从特定模式的数据库中获取用户特定数据。

谢谢。

P.S. 我使用的是PostgreSql,这就是表名为什么要小写的原因。


你能发布一下你使用IModelCacheKeyFactory的解决方法吗? - Zinov
@Zinov,我已经完成了,但请小心处理。https://dev59.com/21kS5IYBdhLWcg3wvI20#50529432 - user3272018
11个回答

33

你已经在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);

5
如我之前所写,将模式名称放在 OnModelCreating 中没有问题,但问题在于这个方法只会被调用一次,因此下一个创建的上下文将使用相同的模式。不过,也许我在你的回复中漏掉了一些重要的东西? - user3272018
我明白你的意思,你说得对,我的先前回答并没有包含解决OnModelCreating问题的方法,但是我在博客上搜索了这个问题的解决方案,并找到了OnConfiguring方法的代码,我修改了我的答案,请检查一下并告诉我是否有用 链接 - H. Herzl
这可能适用于小型项目,但我无法想象维护MapProduct方法以处理数百个表格,更不用提所有可能的设置和迁移了... - Tomas Voracek
你说得很好,是在谈论为这些方法编写代码还是维护大型代码文件? - H. Herzl
AppSettings类属于哪个命名空间?谢谢。 - Kate

14

定义您的上下文并将架构传递给构造函数。

在 OnModelCreating 中设置默认架构。

   public class MyContext : DbContext , IDbContextSchema
    {
        private readonly string _connectionString;
        public string Schema {get;}

        public MyContext(string connectionString, string schema)
        {
            _connectionString = connectionString;
            Schema = schema;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.ReplaceService<IModelCacheKeyFactory, DbSchemaAwareModelCacheKeyFactory>();
                optionsBuilder.UseSqlServer(_connectionString);
            }

            base.OnConfiguring(optionsBuilder);
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.HasDefaultSchema(Schema);
            
            // ... model definition ...
        }
    }

实现你的IModelCacheKeyFactory。

public class DbSchemaAwareModelCacheKeyFactory : IModelCacheKeyFactory
    {
        
        public object Create(DbContext context)
        {
            return new {
                Type = context.GetType(),
                Schema = context is IDbContextSchema schema 
                    ? schema.Schema 
                    :  null
            };
        }
    }

在 OnConfiguring 中,使用您自定义的实现替换 IModelCacheKeyFactory 的默认实现。

使用 IModelCacheKeyFactory 的默认实现时,仅在第一次实例化上下文时执行 OnModelCreating 方法,然后结果会被缓存。 通过更改实现,您可以修改如何缓存和检索 OnModelCreating 的结果。通过在缓存键中包括模式,您可以为传递给上下文构造函数的每个不同模式字符串获取执行并缓存 OnModelCreating。

// Get a context referring SCHEMA1
var context1 = new MyContext(connectionString, "SCHEMA1");
// Get another context referring SCHEMA2
var context2 = new MyContext(connectionString, "SCHEMA2");

这就是我的字面回答 - C Rudolph

10

抱歉,大家。我本应该在此之前发布我的解决方案,但由于某些原因,我没有这样做,现在在这里呈现。

但是

请记住,由于该解决方案尚未经过任何人的审核或生产验证,所以任何地方都可能存在问题,可能我会在这里得到一些反馈。

在项目中,我使用了 ASP .NET Core 1


关于我的数据库结构。我有两个上下文。第一个包含有关用户的信息(包括他们应该访问的数据库模式),第二个包含特定于用户的数据。

Startup.cs 中,我添加了这两个上下文。

public void ConfigureServices(IServiceCollection 
    services.AddEntityFrameworkNpgsql()
        .AddDbContext<SharedDbContext>(options =>
            options.UseNpgsql(Configuration["MasterConnection"]))
        .AddDbContext<DomainDbContext>((serviceProvider, options) => 
            options.UseNpgsql(Configuration["MasterConnection"])
                .UseInternalServiceProvider(serviceProvider));
...
    services.Replace(ServiceDescriptor.Singleton<IModelCacheKeyFactory, MultiTenantModelCacheKeyFactory>());
    services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

请注意UseInternalServiceProvider的部分,这是由Nero Sule建议的,并附有以下说明:

在EFC 1发布周期的最后,EF团队决定从默认服务集合中删除EF的服务(AddEntityFramework().AddDbContext()),这意味着需要使用EF自己的服务提供程序来解析服务,而不是应用程序服务提供程序。

要强制EF使用您的应用程序服务提供程序,您需要先将EF的服务与数据提供程序一起添加到服务集合中,然后配置DBContext以使用内部服务提供程序。

现在我们需要MultiTenantModelCacheKeyFactory

public class MultiTenantModelCacheKeyFactory : ModelCacheKeyFactory {
    private string _schemaName;
    public override object Create(DbContext context) {
        var dataContext = context as DomainDbContext;
        if(dataContext != null) {
            _schemaName = dataContext.SchemaName;
        }
        return new MultiTenantModelCacheKey(_schemaName, context);
    }
}

其中DomainDbContext是包含用户特定数据的上下文

public class MultiTenantModelCacheKey : ModelCacheKey {
    private readonly string _schemaName;
    public MultiTenantModelCacheKey(string schemaName, DbContext context) : base(context) {
        _schemaName = schemaName;
    }
    public override int GetHashCode() {
        return _schemaName.GetHashCode();
    }
}

此外,我们需要稍微更改上下文本身,以使其具有模式感知性:

public class DomainDbContext : IdentityDbContext<ApplicationUser> {
    public readonly string SchemaName;
    public DbSet<Foo> Foos{ get; set; }

    public DomainDbContext(ICompanyProvider companyProvider, DbContextOptions<DomainDbContext> options)
        : base(options) {
        SchemaName = companyProvider.GetSchemaName();
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.HasDefaultSchema(SchemaName);
        base.OnModelCreating(modelBuilder);
    }
}

共享上下文严格与 shared 模式绑定:

public class SharedDbContext : IdentityDbContext<ApplicationUser> {
    private const string SharedSchemaName = "shared";
    public DbSet<Foo> Foos{ get; set; }
    public SharedDbContext(DbContextOptions<SharedDbContext> options)
        : base(options) {}
    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.HasDefaultSchema(SharedSchemaName);
        base.OnModelCreating(modelBuilder);
    }
}

ICompanyProvider 负责获取用户模式名称。是的,我知道这段代码离完美还有很远。

public interface ICompanyProvider {
    string GetSchemaName();
}

public class CompanyProvider : ICompanyProvider {
    private readonly SharedDbContext _context;
    private readonly IHttpContextAccessor _accesor;
    private readonly UserManager<ApplicationUser> _userManager;

    public CompanyProvider(SharedDbContext context, IHttpContextAccessor accesor, UserManager<ApplicationUser> userManager) {
        _context = context;
        _accesor = accesor;
        _userManager = userManager;
    }
    public string GetSchemaName() {
        Task<ApplicationUser> getUserTask = null;
        Task.Run(() => {
            getUserTask = _userManager.GetUserAsync(_accesor.HttpContext?.User);
        }).Wait();
        var user = getUserTask.Result;
        if(user == null) {
            return "shared";
        }
        return _context.Companies.Single(c => c.Id == user.CompanyId).SchemaName;
    }
}

如果我没有漏掉任何东西,那就是这样。现在每个经过身份验证的用户请求都将使用正确的上下文。

希望这有所帮助。


2
你在 OnModelCreating 中设置了 DefaultSchema... 但是那个方法只会被调用一次。那么你如何根据请求动态地更改模式呢?我不明白这个解决方案是如何工作的。 - Jarrich Van de Voorde
@JarrichVandeVoorde,模型在EF内部被缓存,但在这种情况下,MultiTenantModelCacheKeyFactory配置了EF并且缓存根据当前公司(或与当前公司关联的模式)进行分割。当dbcontext被实例化时,键是不同的,如果在缓存中找不到,则会再次调用OnModelCreating。 这种方法的负面影响是每个新公司都会增加缓存(在我的情况下,这是内存溢出异常的原因)。 - t.ouvre
@user3272018,非常感谢您。这是一个很棒的想法。我的场景是动态创建模式并重新加载映射。 a. 我正在研究ModelSource,并尝试在ModelSource中清除缓存。但更改缓存键是一种更简单的方法 :) b. UseInternalServiceProvider()很棒,它解决了EF Core中的问题,即ServiceProvider将在DbContext初始化期间初始化。 - Robin Ding

5

在EFCore中,要想处理自定义模型,需要花费几个小时来弄清楚。似乎有很多混乱关于正确实现的方式。我相信处理自定义模型的简单和正确方式是替换默认的IModelCacheKeyFactory服务,就像我下面展示的那样。在我的示例中,我设置了自定义表名。

  1. 在您的上下文类中创建一个ModelCacheKey变量。
  2. 在您的上下文构造函数中,设置ModelCacheKey变量。
  3. 创建一个从IModelCacheKeyFactory继承的类,并使用ModelCacheKey(MyModelCacheKeyFactory)。
  4. 在OnConfiguring方法(MyContext)中,替换默认的IModelCacheKeyFactory。
  5. 在OnModelCreating方法(MyContext)中,使用ModelBuilder定义所需内容。
public class MyModelCacheKeyFactory : IModelCacheKeyFactory
{
    public object Create(DbContext context)
        => context is MyContext myContext ?
        (context.GetType(), myContext.ModelCacheKey) :
        (object)context.GetType();
}

public partial class MyContext : DbContext
{
     public string Company { get; }
     public string ModelCacheKey { get; }
     public MyContext(string connectionString, string company) : base(connectionString) 
     { 
         Company = company;
         ModelCacheKey = company; //the identifier for the model this instance will use
     }

     protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
     {
         //This will create one model cache per key
         optionsBuilder.ReplaceService<IModelCacheKeyFactory, MyModelCacheKeyFactory();
     }

     protected override void OnModelCreating(ModelBuilder modelBuilder)
     {
         modelBuilder.Entity<Order>(entity => 
         { 
             //regular entity mapping 
         });

         SetCustomConfigurations(modelBuilder);
     }

     public void SetCustomConfigurations(ModelBuilder modelBuilder)
     {
         //Here you will set the schema. 
         //In my example I am setting custom table name Order_CompanyX

         var entityType = typeof(Order);
         var tableName = entityType.Name + "_" + this.Company;
         var mutableEntityType = modelBuilder.Model.GetOrAddEntityType(entityType);
         mutableEntityType.RemoveAnnotation("Relational:TableName");
         mutableEntityType.AddAnnotation("Relational:TableName", tableName);
     }
}

结果是您上下文的每个实例都会导致 efcore 基于 ModelCacheKey 变量进行缓存。


5

有几种方法可以做到这一点:

  • 外部构建模型,并通过 DbContextOptionsBuilder.UseModel() 传递进去
  • 替换 IModelCacheKeyFactory 服务,使用一个考虑架构的服务

4
请提供一些详细信息或链接到一些文档/博客/教程吗? - user3272018
@user3272018 我也遇到了同样的问题,EF Core 中关于如何正确实现 IModelCacheKeyFactory 的文档和示例都没有提供。 - Tomas Voracek
1
哦,最终我做到了。稍后我会提供那段代码作为自我回答。我相信这不是达到我的目标的完美方式,但它有效。也许甚至有人可以改进我的解决方案。很抱歉我没有早点完成它。 - user3272018
1
@Diego,正如我在问题中提到的,我已经在这里分享了我的解决方案(https://dev59.com/21kS5IYBdhLWcg3wvI20#50529432)。 - user3272018
@user3272018 谢谢,我只需要改一些小细节,但它现在运行得很好 ;) - Diego Garcia
显示剩余2条评论

3
我觉得这篇博客对你可能有用。非常好!:) https://romiller.com/2011/05/23/ef-4-1-multi-tenant-with-code-first/ 这篇博客是基于EF4的,我不确定它是否能够与EF Core很好地配合使用。
public class ContactContext : DbContext
{
    private ContactContext(DbConnection connection, DbCompiledModel model)
        : base(connection, model, contextOwnsConnection: false)
    { }

    public DbSet<Person> People { get; set; }
    public DbSet<ContactInfo> ContactInfo { get; set; }

    private static ConcurrentDictionary<Tuple<string, string>, DbCompiledModel> modelCache
        = new ConcurrentDictionary<Tuple<string, string>, DbCompiledModel>();

    /// <summary>
    /// Creates a context that will access the specified tenant
    /// </summary>
    public static ContactContext Create(string tenantSchema, DbConnection connection)
    {
        var compiledModel = modelCache.GetOrAdd(
            Tuple.Create(connection.ConnectionString, tenantSchema),
            t =>
            {
                var builder = new DbModelBuilder();
                builder.Conventions.Remove<IncludeMetadataConvention>();
                builder.Entity<Person>().ToTable("Person", tenantSchema);
                builder.Entity<ContactInfo>().ToTable("ContactInfo", tenantSchema);

                var model = builder.Build(connection);
                return model.Compile();
            });

        return new ContactContext(connection, compiledModel);
    }

    /// <summary>
    /// Creates the database and/or tables for a new tenant
    /// </summary>
    public static void ProvisionTenant(string tenantSchema, DbConnection connection)
    {
        using (var ctx = Create(tenantSchema, connection))
        {
            if (!ctx.Database.Exists())
            {
                ctx.Database.Create();
            }
            else
            {
                var createScript = ((IObjectContextAdapter)ctx).ObjectContext.CreateDatabaseScript();
                ctx.Database.ExecuteSqlCommand(createScript);
            }
        }
    }
}

这些代码的主要思想是提供一种静态方法,通过不同的模式创建不同的DbContext,并使用特定标识符进行缓存。

2

也许我回答有点晚了

我的问题是如何处理具有相同结构的不同模式,比如多租户。

当我尝试为不同的模式创建相同上下文的不同实例时,Entity Framework 6就会出现问题。第一次创建dbContext时,它就被捕获了,然后对于以下实例,它们将使用不同的模式名称进行创建,但onModelCreating从未被调用,这意味着每个实例都指向先前缓存的预生成视图,指向第一个模式。

然后我意识到,为每个模式创建继承自myDBContext的新类将解决我的问题,通过克服实体框架捕获问题为每个模式创建一个新的上下文,但随之而来的问题是我们将以硬编码模式结束,在需要添加另一个模式时,需要添加更多类并重新编译和发布应用程序的新版本,导致代码可伸缩性的另一个问题。

因此,我决定进一步创建、编译并在运行时将类添加到当前解决方案中。

以下是代码:

public static MyBaseContext CreateContext(string schema)
{
    MyBaseContext instance = null;
    try
    {
        string code = $@"
            namespace MyNamespace
            {{
                using System.Collections.Generic;
                using System.Data.Entity;

                public partial class {schema}Context : MyBaseContext
                {{
                    public {schema}Context(string SCHEMA) : base(SCHEMA)
                    {{
                    }}

                    protected override void OnModelCreating(DbModelBuilder modelBuilder)
                    {{
                        base.OnModelCreating(modelBuilder);
                    }}
                }}
            }}
        ";

        CompilerParameters dynamicParams = new CompilerParameters();

        Assembly currentAssembly = Assembly.GetExecutingAssembly();
        dynamicParams.ReferencedAssemblies.Add(currentAssembly.Location);   // Reference the current assembly from within dynamic one
                                                                            // Dependent Assemblies of the above will also be needed
        dynamicParams.ReferencedAssemblies.AddRange(
            (from holdAssembly in currentAssembly.GetReferencedAssemblies()
             select Assembly.ReflectionOnlyLoad(holdAssembly.FullName).Location).ToArray());

        // Everything below here is unchanged from the previous
        CodeDomProvider dynamicLoad = CodeDomProvider.CreateProvider("C#");
        CompilerResults dynamicResults = dynamicLoad.CompileAssemblyFromSource(dynamicParams, code);

        if (!dynamicResults.Errors.HasErrors)
        {
            Type myDynamicType = dynamicResults.CompiledAssembly.GetType($"MyNamespace.{schema}Context");
            Object[] args = { schema };
            instance = (MyBaseContext)Activator.CreateInstance(myDynamicType, args);
        }
        else
        {
            Console.WriteLine("Failed to load dynamic assembly" + dynamicResults.Errors[0].ErrorText);
        }
    }
    catch (Exception ex)
    {
        string message = ex.Message;
    }
    return instance;
}

我希望这能帮助某些人节省时间。

2

您可以在固定模式表上使用Table属性。

对于模式更改的表,您不能使用该属性,需要通过ToTable流畅的API进行配置。如果禁用模型缓存(或编写自己的缓存),则模式可能会在每个请求上更改,因此在上下文创建时(每次)您可以指定模式。

这是基本思路。

class MyContext : DbContext
{
    public string Schema { get; private set; }

    public MyContext(string schema) : base()
    {

    }

    // Your DbSets here
    DbSet<Emp> Emps { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Emp>()
            .ToTable("Emps", Schema);
    }
}

现在,在创建上下文之前,您可以有一些不同的方法来确定模式名称。
例如,您可以将您的“系统表”放在不同的上下文中,因此在每个请求中,您可以从用户名称中使用系统表检索模式名称,然后在正确的模式上创建工作上下文(您可以在上下文之间共享表)。
您可以将系统表与上下文分离,并使用ADO .Net访问它们。
可能还有其他几个解决方案。
您也可以在这里查看:
Multi-Tenant With Code First EF6
和您可以谷歌ef multi tenant
编辑
还有一个模型缓存问题(我忘记了这个)。 您必须禁用模型缓存或更改缓存的行为。

谢谢您的回复,但据我所知,在每个请求中都无法通过OnModelCreating方法更改模式名称,因为该方法只被调用一次。实际上,我使用的是EF Core,但OnModelCreating的行为是相同的。@bricelam提到了两种解决方法,所以我对更详细的解释很感兴趣,因为EF的开发人员应该比其他人更了解EF,对吧? - user3272018
在你发布的SO问题中,有一个名为IDbModelCacheKeyProvider的接口,它似乎类似于IModelCacheKeyFactory。也许这是解决我的问题的关键。我错过了那篇文章--谢谢。 - user3272018
你说得对!!! 我忘记了模型缓存! 还要注意性能问题(你可以在网上找到一些文章,也可以在Stack Overflow上找到)。 - bubi

0

MVC Core 2.1 更新

您可以从具有多个架构的数据库创建模型。系统对架构名称不太敏感。相同命名的表格会添加“1”。 “dbo”是假定的模式,因此您无需在表格名称前加上任何前缀。

您需要自己重命名模型文件名和类名。

在 PM 控制台中

Scaffold-DbContext "Data Source=localhost;Initial Catalog=YourDatabase;Integrated Security=True" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -force -Tables TableA, Schema1.TableA

0

如果数据库之间唯一的区别是模式名称,那么消除问题的最简单方法是删除在OnModelCreating方法中设置默认模式的代码行:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    ...
    modelBuilder.HasDefaultSchema("YourSchemaName"); <-- remove or comment this line
    ...
}

在这种情况下,EF Core 运行的 SQL 查询将不会在其 FROM 子句中包含模式名称。然后,您将能够编写一个方法,根据您的自定义条件设置正确的 DbContext。
以下是我用来连接到具有相同数据库结构的不同 Oracle 数据库的示例(简而言之,假设在 Oracle 中模式与用户相同)。如果您使用另一个数据库,只需放置正确的连接字符串,然后进行修改即可。
private YourDbContext SetDbContext()
{
    string connStr = @"Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=server_ip)(PORT=1521)))(CONNECT_DATA=(SID = server_sid)));User Id=server_user ;Password=server_password";

    //You can get db connection details e.g. from app config
    List<string> connections = config.GetSection("DbConneections");
    string serverIp;
    string dbSid;
    string dBUser;
    string dbPassword;

    /* some logic to choose a connection from config and set up string variables for a connection*/

    connStr = connStr.Replace("server_ip", serverIp);
    connStr = connStr.Replace("server_sid", dbSid);
    connStr = connStr.Replace("server_user", dBUser);
    connStr = connStr.Replace("server_password", dbPassword);

    var dbContext = dbContextFactory.CreateDbContext();
    dbContext.Database.CloseConnection();
    dbContext.Database.SetConnectionString(connStr);

    return dbContext;
}

最后,您将能够在需要的地方设置所需的dbContext,在调用此方法之前,您还可以传递一些参数以帮助您选择正确的数据库。

那怎么设置所需的模式名称呢? - Gert Arnold
@GertArnold我编辑了我的答案,请看一下。 - maniacz
仍然与设置模式无关。请在此问题的上下文中检查“模式”一词的含义。 - Gert Arnold
有时人们倾向于通过“添加”某些东西来解决问题,而似乎忽略了基于“减法”的解决方案。在这个问题中,我可以想象你可能会遇到标题中描述的这种问题,正如我在答案中提到的,如果唯一的区别是模式名称,那么你基本上有不同的数据库具有相同的实体,你可以通过EF Core摆脱设置模式并解决问题,无论如何,它解决了我的问题,所以我决定分享这种方法。 - maniacz
你似乎没有意识到 OP 需要 设置模式,因为涉及到多租户。没有模式前缀的查询不符合要求,而在运行时更改连接字符串也不是问题所在。 - Gert Arnold

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接