J'ai récemment besoin de résoudre ce problème pour développer une connexion multi-locataire fiable capable de supporter non seulement le locataire DB se trouvant sur des serveurs différents (qui peuvent être résolus en utilisant la chaîne de connexion), mais aussi le soutien locataire -par-schéma. Le bit délicat ici est que votre mappage d'entité doit refléter le nom du schéma pour résoudre la table. Cela suppose que tous les locataires utilisent le même schéma de base de données, mais seulement des noms de schéma différents.
Dans mon cas, j'utilisais le Mehdi.me DbContextScope qui prenait partiellement en charge ce dont j'avais besoin puisque vous pouviez surcharger et fournir une implémentation IDbContextFactory
pour créer votre DbContexts en utilisant la chaîne de connexion appropriée. Le deuxième bit j'ai construit un peu d'une extension aux usines pour s'assurer que les détails de schéma de locataire pourraient passer à l'initialisation de la configuration d'entité.
N'hésitez pas à jeter un coup d'oeil pour voir si cela peut convenir à votre projet, ou vous donner des idées pour obtenir ce dont vous avez besoin. (https://github.com/StevePy/DbContextScope)
La mise en œuvre prend un peu de configuration, et évidemment s'y habituer au motif DbContextFactory/DbContextLocator pour l'unité de travail, mais vous avez besoin à peu près la configuration suivante:
Création d'une classe pour représenter votre détails de connexion du locataire mettant en œuvre IDbTenant
. Cette classe serait liée à l'instance de client hébergé en cours. Elle renvoie la chaîne de connexion et le nom de schéma de l'emplacement des tables du locataire.
Une implémentation de IDbContextFactory
dans votre projet qui va construire l'instance DbContext
. Cette usine accepte normalement un constructeur par défaut, une chaîne de connexion, et maintenant une instance de la IDbTenant
créé est l'étape 1.
Initialiser votre IoC si elle est présente pour initialiser le DbContextScopeFactory
avec le IDbContextFactory
créé à l'étape 2. Cela ressemblerait à quelque chose comme:
ioc.Register<IDbContextScopeFactory>(()=> {new DbContextScopeFactory(new SqlServerTenantDbContextFactory());});
Où SqlServerTenantDbContextFactory
est la mise en œuvre créée à l'étape 2. Ce qui précède est à peu près le processus d'inscription pour Autofac IoC. Essentiellement, vous voulez juste vous assurer qu'un DbContextScopeFactory
est instancié, vous lui fournissez votre DbContextFactory
.
Une implémentation de ContextFactory.
public class SqlServerTenantDbContextFactory : IDbContextFactory
{
TDbContext IDbContextFactory.CreateDbContext<TDbContext>()
{
return (TDbContext)Activator.CreateInstance<TDbContext>();
}
TDbContext IDbContextFactory.CreateDbContext<TDbContext>(IDbTenant tenant)
{
var connection = DbProviderFactories.GetFactory("System.Data.SqlClient").CreateConnection();
// based on the provider set up in <providers> configration under <entityFramework>...
connnection.ConnectionString = tenant?.ConnectionString;
return (TDbContext)Activator.CreateInstance(typeof(TDbContext), tenant, connection, true);
}
TDbContext IDbContextFactory.CreateDbContext<TDbContext>(string connectionString)
{
var connection = DbProviderFactories.GetFactory("System.Data.SqlClient").CreateConnection();
connnection.ConnectionString = connectionString;
return (TDbContext)Activator.CreateInstance(typeof(TDbContext), connection, true);
}
}
La dernière modification de la configuration est pour le EntityTypeConfiguration
d'ajouter un constructeur qui accepte un IDbTenant
et ajoutez l'attribut [ImportingConstructor]
à ce constructeur. ContextFactory s'occupera du reste. Ainsi, par exemple, étant donné une entité appelée « Ordre », vous définir une configuration de type d'entité comme:
public class OrderConfiguration : EntityTypeConfiguration<Order>
{
[ImportingConstructor]
public OrderConfiguration(IDbTenant tenant)
: base()
{
ToTable("Orders", tenant.SchemaName);
// HasKey(...);
// HasMany(...);
// etc. etc. etc.
}
}
Je ne sais pas si cela peut être adapté le code d'abord pour servir les mises en œuvre, mais je vous en doute depuis que pattern veut prendre la responsabilité de la définition et de la migration du schéma db, ce qui serait un désordre quand il s'agit de basculer entre les schémas ou les serveurs.
C'est probablement beaucoup, surtout si vous n'avez pas d'expérience avec le Mebdi.me DbContextScope, mais j'espère que cela pourrait vous donner quelques idées.
Une fois ces sont fixés, le seul changement du modèle Mehdi.me normale est que lorsque vous allez récupérer le contexte à l'aide du DbContextLocator
, le passer, une instance de l'instance IDbTenant
:
private MyAppContext Context
{
get { return ContextLocator.Get<MyAppContext>(_tenant); }
}
où _tenant est initialisé en fonction de votre locataire connecté. (C'est-à-dire la stratégie d'identification des locataires tirant les détails de l'authentification OWIN, ou de l'état de la session ...) A partir de là, l'usine de contexte prend le relais et initialise votre contexte en utilisant les détails du locataire.
Vous ne savez pas comment obtenir le schéma en cours d'exécution, mais vous pouvez créer votre contexte en fonction d'une base de données existante afin de pouvoir ajouter différents contextes dans votre application, puis passer d'un environnement à l'autre. Mais normalement, vous devez coder en fonction d'un schéma spécifié. Vous pouvez ainsi créer des classes métier pour différents contextes et simplement activer le moteur d'exécution. –