- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我们的组织需要有一个单一的数据库, Multi-Tenancy
(按表架构,而不是按租户 ID)架构。
这里有一篇关于开始使用这种东西的很棒的文章: http://romiller.com/2011/05/23/ef-4-1-multi-tenant-with-code-first/
文章中间,是这样写的:
You’ll notice (probably with some dismay) we need to write code to configure the table schema for each entity. Admittedly there aren’t many magic unicorns grazing around this code… in future versions of EF we will be able to replace this with a much cleaner custom convention.
我们的目标是尽可能以最简洁的方式拥有一个上下文类,我们可以使用它来连接到具有相同模型的多个模式。
(请注意,modelBuilder.HasDefaultSchema 似乎还不够,因为它仅适用于第一次 EF 初始化上下文并运行 OnModelCreating)
EF5 或 EF6 中是否存在上述更清洁的自定义约定?
还是有更简洁的方式来处理这个问题?
注意:我也在开发论坛上问过这个问题,因为它似乎与 EF 的方向有关,但想看看这里是否有人有其他选择。
注意 2:我不担心迁移,我们会单独处理。
最佳答案
OnModelCreating
中的属性 modelBuilder.HasDefaultSchema
就足够了 如果 您在 DbContext 上实现了
。模型创建一次,然后由 IDbModelCacheKeyProvider
EntityFramwork
在内部缓存,您可以为缓存定义自己的键。将模式名称作为模型缓存键,EF 将通过每个不同的缓存键(在我们的例子中为模式)创建一个模型。这是我的概念验证代码:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TenantDataModel;
namespace TenantDataContext
{
public class TenantDataCtx : DbContext, IDbModelCacheKeyProvider
{
#region Construction
public static TenantDataCtx Create(string databaseServer, string databaseName, string databaseUserName, string databasePassword, Guid tenantId)
{
var connectionStringBuilder = new System.Data.SqlClient.SqlConnectionStringBuilder();
connectionStringBuilder.DataSource = databaseServer;
connectionStringBuilder.InitialCatalog = databaseName;
connectionStringBuilder.UserID = databaseUserName;
connectionStringBuilder.Password = databasePassword;
string connectionString = connectionStringBuilder.ToString();
return new TenantDataCtx(connectionString, tenantId);
}
// Used by EF migrations
public TenantDataCtx()
{
Database.SetInitializer<TenantDataCtx>(null);
}
internal TenantDataCtx(string connectionString, Guid tenantId)
: base(connectionString)
{
Database.SetInitializer<TenantDataCtx>(null);
this.SchemaName = tenantId.ToString("D");
}
public string SchemaName { get; private set; }
#endregion
#region DataSet Properties
public DbSet<TestEntity> TestEntities { get { return this.Set<TestEntity>(); } }
#endregion
#region Overrides
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
if (this.SchemaName != null)
{
modelBuilder.HasDefaultSchema(this.SchemaName);
}
base.OnModelCreating(modelBuilder);
}
#endregion
#region IDbModelCacheKeyProvider Members
public string CacheKey
{
get { return this.SchemaName; }
}
#endregion
}
}
此外,我还找到了一种使用 EF 迁移的方法。我对我的解决方案不是很满意,但目前似乎没有其他可用的解决方案。
using System;
using System.Collections.Generic;
using System.Data.Entity.SqlServer;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TenantDatabaseManager
{
public class SqlServerSchemaAwareMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
private string _schema;
public SqlServerSchemaAwareMigrationSqlGenerator(string schema)
{
_schema = schema;
}
protected override void Generate(System.Data.Entity.Migrations.Model.AddColumnOperation addColumnOperation)
{
string newTableName = _GetNameWithReplacedSchema(addColumnOperation.Table);
var newAddColumnOperation = new System.Data.Entity.Migrations.Model.AddColumnOperation(newTableName, addColumnOperation.Column, addColumnOperation.AnonymousArguments);
base.Generate(newAddColumnOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.AddPrimaryKeyOperation addPrimaryKeyOperation)
{
addPrimaryKeyOperation.Table = _GetNameWithReplacedSchema(addPrimaryKeyOperation.Table);
base.Generate(addPrimaryKeyOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.AlterColumnOperation alterColumnOperation)
{
string tableName = _GetNameWithReplacedSchema(alterColumnOperation.Table);
var newAlterColumnOperation = new System.Data.Entity.Migrations.Model.AlterColumnOperation(tableName, alterColumnOperation.Column, alterColumnOperation.IsDestructiveChange);
base.Generate(newAlterColumnOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.DropPrimaryKeyOperation dropPrimaryKeyOperation)
{
dropPrimaryKeyOperation.Table = _GetNameWithReplacedSchema(dropPrimaryKeyOperation.Table);
base.Generate(dropPrimaryKeyOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.CreateIndexOperation createIndexOperation)
{
string name = _GetNameWithReplacedSchema(createIndexOperation.Table);
createIndexOperation.Table = name;
base.Generate(createIndexOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.CreateTableOperation createTableOperation)
{
string newTableName = _GetNameWithReplacedSchema(createTableOperation.Name);
var newCreateTableOperation = new System.Data.Entity.Migrations.Model.CreateTableOperation(newTableName, createTableOperation.AnonymousArguments);
newCreateTableOperation.PrimaryKey = createTableOperation.PrimaryKey;
foreach (var column in createTableOperation.Columns)
{
newCreateTableOperation.Columns.Add(column);
}
base.Generate(newCreateTableOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.RenameTableOperation renameTableOperation)
{
string oldName = _GetNameWithReplacedSchema(renameTableOperation.Name);
string newName = renameTableOperation.NewName.Split(new char[] { '.' }).Last();
var newRenameTableOperation = new System.Data.Entity.Migrations.Model.RenameTableOperation(oldName, newName, renameTableOperation.AnonymousArguments);
base.Generate(newRenameTableOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.RenameIndexOperation renameIndexOperation)
{
string tableName = _GetNameWithReplacedSchema(renameIndexOperation.Table);
var newRenameIndexOperation = new System.Data.Entity.Migrations.Model.RenameIndexOperation(tableName, renameIndexOperation.Name, renameIndexOperation.NewName);
base.Generate(newRenameIndexOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.AddForeignKeyOperation addForeignKeyOperation)
{
addForeignKeyOperation.DependentTable = _GetNameWithReplacedSchema(addForeignKeyOperation.DependentTable);
addForeignKeyOperation.PrincipalTable = _GetNameWithReplacedSchema(addForeignKeyOperation.PrincipalTable);
base.Generate(addForeignKeyOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.DropColumnOperation dropColumnOperation)
{
string newTableName = _GetNameWithReplacedSchema(dropColumnOperation.Table);
var newDropColumnOperation = new System.Data.Entity.Migrations.Model.DropColumnOperation(newTableName, dropColumnOperation.Name, dropColumnOperation.AnonymousArguments);
base.Generate(newDropColumnOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.RenameColumnOperation renameColumnOperation)
{
string newTableName = _GetNameWithReplacedSchema(renameColumnOperation.Table);
var newRenameColumnOperation = new System.Data.Entity.Migrations.Model.RenameColumnOperation(newTableName, renameColumnOperation.Name, renameColumnOperation.NewName);
base.Generate(newRenameColumnOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.DropTableOperation dropTableOperation)
{
string newTableName = _GetNameWithReplacedSchema(dropTableOperation.Name);
var newDropTableOperation = new System.Data.Entity.Migrations.Model.DropTableOperation(newTableName, dropTableOperation.AnonymousArguments);
base.Generate(newDropTableOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.DropForeignKeyOperation dropForeignKeyOperation)
{
dropForeignKeyOperation.PrincipalTable = _GetNameWithReplacedSchema(dropForeignKeyOperation.PrincipalTable);
dropForeignKeyOperation.DependentTable = _GetNameWithReplacedSchema(dropForeignKeyOperation.DependentTable);
base.Generate(dropForeignKeyOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.DropIndexOperation dropIndexOperation)
{
dropIndexOperation.Table = _GetNameWithReplacedSchema(dropIndexOperation.Table);
base.Generate(dropIndexOperation);
}
private string _GetNameWithReplacedSchema(string name)
{
string[] nameParts = name.Split('.');
string newName;
switch (nameParts.Length)
{
case 1:
newName = string.Format("{0}.{1}", _schema, nameParts[0]);
break;
case 2:
newName = string.Format("{0}.{1}", _schema, nameParts[1]);
break;
case 3:
newName = string.Format("{0}.{1}.{2}", _schema, nameParts[1], nameParts[2]);
break;
default:
throw new NotSupportedException();
}
return newName;
}
}
}
这就是我使用 SqlServerSchemaAwareMigrationSqlGenerator
的方式:
// Update TenantDataCtx
var tenantDataMigrationsConfiguration = new DbMigrationsConfiguration<TenantDataContext.TenantDataCtx>();
tenantDataMigrationsConfiguration.AutomaticMigrationsEnabled = false;
tenantDataMigrationsConfiguration.SetSqlGenerator("System.Data.SqlClient", new SqlServerSchemaAwareMigrationSqlGenerator(schemaName));
tenantDataMigrationsConfiguration.SetHistoryContextFactory("System.Data.SqlClient", (existingConnection, defaultSchema) => new HistoryContext(existingConnection, schemaName));
tenantDataMigrationsConfiguration.TargetDatabase = new System.Data.Entity.Infrastructure.DbConnectionInfo(connectionString, "System.Data.SqlClient");
tenantDataMigrationsConfiguration.MigrationsAssembly = typeof(TenantDataContext.TenantDataCtx).Assembly;
tenantDataMigrationsConfiguration.MigrationsNamespace = "TenantDataContext.Migrations.TenantData";
DbMigrator tenantDataCtxMigrator = new DbMigrator(tenantDataMigrationsConfiguration);
tenantDataCtxMigrator.Update();
来自德国的问候,
托比亚斯
关于c# - 使用 Code First EF6 的 Multi-Tenancy ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19458943/
对于我积极维护的客户基于 Web 的 CRM 的分支机构数量不断增加的 Multi-Tenancy ,我需要做出一个艰难的数据库设计决策。 我很早就决定为每个分支使用具有单独数据库的单独应用程序,因为
在 Spring 3 应用程序中,我试图通过 Hibernate 4 的原生 MultiTenantConnectionProvider 实现 Multi-Tenancy 。和 CurrentTena
在neo4j中实现 Multi-Tenancy 的最佳方法是什么? 我看过 Tinkerpop 和 Spring Data。 我有多个客户端,我想将客户端信息存储在自己的数据库中以确保安全。 我不想使
我正在一个小团队工作,设计一个流程管理系统,供同一行业内的几个不同客户使用。系统的目标和客户的高水平要求非常相似。然而,正如预期的那样——一旦我们开始深入挖掘他们的个人需求,我们最终会为每个客户进行一
我们正在开发一个“中间层”来替换现有的业务逻辑/数据访问层。我们面临的设计问题之一是,我们需要以一种允许多个客户的数据库和/或中间层部件作为我们托管产品的一部分存在于同一服务器上的方式进行设计。托管环
我有一个非常简单的项目,其中包含一个具有以下 models.py 的应用程序: class UserAccount: user = models.OneToOneField(User)
在我的应用程序中,我想为不同的客户实现 Multi-Tenancy 支持。 我有一个节点树,希望为不同的客户实现私有(private)树的支持。 Neo4j 是否支持 Multi-Tenancy ,或
我似乎找不到任何关于具有多个数据库的 Grails Multitenancy(每个租户一个)的更新资源/指南、插件或示例。我目前被困在 https://grails.org/plugin/multi-
在我的 Laravel 项目中,我使用了 Multi-Tenancy 。对于项目相关的工作,我正在创建一个 laravel 自定义 artisan 命令。我正在尝试在我正在创建的那个 artisan
我想实现 OKTA API 来管理 Multi-Tenancy 环境中的身份验证和 SSO。 每个用户都应该链接到一个租户,并且每个租户都应该配置身份验证策略。有没有办法实现这一目标? 最佳答案 Ok
我在网上阅读了几篇关于 Multi-Tenancy (针对 SaaS 应用程序中的不同客户)的文章(即 this 和 that 和 that)。一旦您决定采用基于鉴别器的方法,hibernate do
我有几个客户端使用的数据库。我真的不希望代理增量键值在客户端之间流血。我希望编号从1开始,并且要针对客户。 我将使用tenant_id的两部分组合键以及增量ID。 为每个租户创建增量 key 的最佳方
我正在开发一个定制的CRM解决方案,该解决方案将通过Web/SaaS模型出售。我预计会有数十或数百个客户使用此解决方案。我将使用MS SQL作为数据库引擎。 选项1是拥有一个数据库,并在表上包括一个T
我开始设计 Multi-Tenancy 系统并且已经阅读了这篇文章: http://msdn.microsoft.com/en-us/library/aa479086.aspx 无论如何,我有几个与身
我正在考虑在 Multi-Tenancy 应用程序中使用 Solr,我想知道是否有任何最佳实践或需要注意的事项? 一个特别的问题是每个租户拥有一个 Solr Core 是否有意义。有大量的 Solr
由于我对 redis 还很陌生,因此我正在尝试探索各种选项,看看如何使用 redis 实现 Multi-Tenancy 。 我在 redisLabs 官方页面上阅读了一些文档,看起来 redis 集群
我们有一个应用程序,可以在 Multi-Tenancy 工作区的服务器上执行大量数据繁重的工作。 以下是它所做的事情: 它从不同文件格式的文件加载数据。 根据定义的逻辑执行幂等规则。 执行处理逻辑,例
我们有一个 Multi-Tenancy Azure 广告应用程序,该应用程序在某些其他租户中不可见。是否有允许第三方应用程序的租户级别设置? 我们从 azure cli 运行以下命令来查看 hte 应
我尝试登录 Azure AD Web 应用程序,当我使用租户内的用户登录时,就可以了,但是当登录其他用户时我得到 AADSTS50020:来自身份提供商“live.com”的用户帐户“********
我正在将 Firebase 视为 MVP Multi-Tenancy 应用的后端。该应用程序将为每个租户提供完全白标,每个租户都注册自己的用户群。肯定会有交叉,用户可以注册多个租户,但是我似乎看不到
我是一名优秀的程序员,十分优秀!