简体   繁体   English

如何在 EF Core 和 C# 中使用数据库分片"

[英]How to use database sharding with EF Core and C#"

I'm currently in the process of converting my 6 years old C# application to .NET Core v3 and EF Core (and also using Blazor).我目前正在将我 6 岁的 C# 应用程序转换为 .NET Core v3 和 EF Core(也使用 Blazor)。 Most of it is working except for the Sharding part.除了分片部分外,大部分都在工作。
Our application creates a new database for each client.我们的应用程序为每个客户创建一个新数据库。 We use more or less this code for it: https://docs.microsoft.com/en-us/azure/sql-database/sql-database-elastic-scale-use-entity-framework-applications-visual-studio我们或多或少地使用了这个代码: https://docs.microsoft.com/en-us/azure/sql-database/sql-database-elastic-scale-use-entity-framework-applications-visual-studio
I'm now trying to convert it to EF Core, but get stuck at this part:我现在正在尝试将其转换为 EF Core,但在这部分卡住了:

        // C'tor to deploy schema and migrations to a new shard
        protected internal TenantContext(string connectionString)
            : base(SetInitializerForConnection(connectionString))
        {
        }

        // Only static methods are allowed in calls into base class c'tors
        private static string SetInitializerForConnection(string connnectionString)
        {
            // We want existence checks so that the schema can get deployed
            Database.SetInitializer<TenantContext<T>>(new CreateDatabaseIfNotExists<TenantContext<T>>());
            return connnectionString;
        }

        // C'tor for data dependent routing. This call will open a validated connection routed to the proper
        // shard by the shard map manager. Note that the base class c'tor call will fail for an open connection
        // if migrations need to be done and SQL credentials are used. This is the reason for the 
        // separation of c'tors into the DDR case (this c'tor) and the internal c'tor for new shards.
        public TenantContext(ShardMap shardMap, T shardingKey, string connectionStr)
            : base(CreateDDRConnection(shardMap, shardingKey, connectionStr), true /* contextOwnsConnection */)
        {
        }

        // Only static methods are allowed in calls into base class c'tors
        private static DbConnection CreateDDRConnection(ShardMap shardMap, T shardingKey, string connectionStr)
        {
            // No initialization
            Database.SetInitializer<TenantContext<T>>(null);

            // Ask shard map to broker a validated connection for the given key
            var conn = shardMap.OpenConnectionForKey<T>(shardingKey, connectionStr, ConnectionOptions.Validate);
            return conn;
        }

The above code doesn't compile because the Database object doesn't exist in this way in EF Core.上面的代码无法编译,因为数据库 object 在 EF Core 中不存在这种方式。 I assume I can simplify it using TenantContext.Database.EnsureCreated();我假设我可以使用TenantContext.Database.EnsureCreated();来简化它。 somewhere.某处。 But I can't figure out how to modify the methods, which to remove, which to change (and how).但我不知道如何修改方法、删除哪些、更改哪些(以及如何)。

Of course, I've been searching for an example using sharding and EF Core but couldn't find it.当然,我一直在寻找使用分片和 EF Core 的示例,但找不到。 Does anybody here has done this before in EF Core and is willing the share?这里有没有人以前在 EF Core 中做过这个并且愿意分享?

I'm specifically looking for what to put in startup.cs and how to create a new sharding/database when I create a new client.我正在专门寻找在startup.cs中放置什么以及在创建新客户端时如何创建新的分片/数据库。

In EF.Core just resolve the shard in OnConfiguring.在 EF.Core 中,只需解析 OnConfiguring 中的分片。 EG例如

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    var con = GetTenantConnection(this.tenantName);

    optionsBuilder.UseSqlServer(con,o => o.UseRelationalNulls());

    base.OnConfiguring(optionsBuilder);
}

Note that if you have a service or factory that returns open DbConnections, then you'll need to Close()/Dispose() them in the DbContext.Dispose().请注意,如果您有一个返回打开的 DbConnections 的服务或工厂,那么您需要在 DbContext.Dispose() 中关闭()/处置()它们。 If you get a connection string or a closed connection then DbContext will take care of closing the connection.如果您获得连接字符串或关闭的连接,则 DbContext 将负责关闭连接。

ASP.NET Core best-practices probably call for injecting an ITenantConfiguration service or somesuch in your DbContext. ASP.NET 核心最佳实践可能需要在您的 DbContext 中注入ITenantConfiguration服务或类似服务。 But the pattern is the same.但模式是一样的。 Just save the injected service instance to a DbContext field and use it in OnConfiguring .只需将注入的服务实例保存到 DbContext 字段并在OnConfiguring中使用它。

With the app that I'm working on, the desired shard is not discoverable until request time (for example, knowing what user is making the request, and then routing that user to their database).对于我正在开发的应用程序,在请求时间之前无法发现所需的分片(例如,知道是什么用户发出请求,然后将该用户路由到他们的数据库)。 This meant that the OnConfiguring solution proposed above was not viable.这意味着上面提出的OnConfiguring解决方案不可行。

I worked around this by using IDbContextFactory<TContext> , and defining an extension on top of it, which sets the connection string based on whatever you want.我通过使用IDbContextFactory<TContext>解决了这个问题,并在它之上定义了一个扩展,它根据你想要的任何内容设置连接字符串。 I believe the database connection is created lazily in EF, and you are able to set the connection string up until the EF first needs to actually connect to the database.我相信数据库连接是在 EF 中延迟创建的,您可以设置连接字符串,直到 EF 首先需要实际连接到数据库。

In my case, it looked something like this:就我而言,它看起来像这样:

var dbContext = _dbContextFactory.CreateDbContext();
var connectionString = $"DataSource={_sqlliteDirectory}/tenant_{tenant.TenantId}.db";

dbContext.Database.SetConnectionString(connectionString);

The downside is that it breaks the database abstraction (this code knows that my database is a local sqllite instance).缺点是它破坏了数据库抽象(这段代码知道我的数据库是本地 sqllite 实例)。 An abstraction was not necessary in this layer of my app, but it is something very solvable if it's required.在我的应用程序的这一层中不需要抽象,但如果需要,它是可以解决的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 EF Core 中使用 C# 9 条记录? - How to use C# 9 records with EF Core? 数据库列的属性(C#、EF Core) - Properties to Database Columns (C#, EF Core) .net core C#在EF Core Database首先生成的模型类上使用动态属性名称 - .net core C# use dynamic property name on EF Core Database first generated model class 在EF CORE(C#.NET CORE)上使用&#39;Average()&#39; - Use 'Average()' On EF CORE (C# .NET CORE) 使用 C# 和 EF Core 避免多次调用数据库 - Avoid multiple calls to database with C# & EF Core C#,EF Core 和 Scaffold-DbContext,如何不为数据库中的默认值列传递 null? - C#, EF Core and Scaffold-DbContext, How to not passing null for a default valued column in the database? 在 EF Core 中,我应该使用数据库查询来连接一对多表还是使用 efcore 在 c# 中手动连接? - In EF Core, should I use database query to join one to many table or use manual joining by efcore in the c#? 多次使用导航属性 C# EF Core - Use navigation properties more than once C# EF Core 如何在 EF Core Find 方法中使用不同的 c# 原始数据类型 - How to use different c# primitive data types in EF Core Find method sql server ISJSON function in linq query C# EF6 Database first 如何使用 - How to use sql server ISJSON function in linq query C# EF6 Database first
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM