2017-10-12 2 views
0

J'utilise actuellement Entity Framework 6 dans mon projet ASP.NET MVC 5. Chaque client que nous avons a son propre schéma de base de données. Pour autant que j'ai trouvé jusqu'à présent, il n'est pas facile de changer le schéma à l'exécution avec Entity Framework. J'ai créé une classe d'aide qui prend essentiellement les fichiers csdl et autres et effectue une recherche et un remplacement du schéma, mais évidemment cela ajoute un impact important sur les performances de l'application.Options ASP.NET MVC 5 Entity Framework ou possibilité de spécifier un schéma lors de l'exécution?

Est-ce que quelqu'un sait comment changer le schéma lors de l'exécution pour EF? Si ce n'est pas supporté ou si un travail possible n'est pas disponible autre que ce que j'ai déjà fait, je vais devoir changer d'outil ORM. Y en a-t-il d'autres qui supportent cela là-bas qui me permettent aussi de générer le modèle hors de la base de données? Parce que créer toutes les classes avec les mappings à partir de zéro prendrait beaucoup de temps.

Merci d'avance.

+1

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. –

Répondre

0

En supposant que les suivantes sont remplies:

  • vous utilisez la base de données SQL Server
  • chacun de votre client utilise compte utilisateur de base de données séparée

Vous pouvez définir le schéma par défaut d'un utilisateur dans SQL Serveur.

https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-user-transact-sql

How to set the default schema of a database in SQL Server 2005?

Et puis changer la chaîne de connexion Entity Framework au moment de l'exécution pour chaque client.

Entity Framework change connection at runtime

0

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:

  1. 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.

  2. 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.

  3. 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());});

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.