web-dev-qa-db-fra.com

Entity Framework: modifier la chaîne de connexion au moment de l'exécution

En supposant qu'il existe une application ASP.NET MVC qui utilise Entity Framework 6 avec une approche en premier code et StructureMap en tant qu'IoC.
Il utilise également le modèle d'unité de travail. Voici les codes:

Classe de domaine

    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }        
 
    }

IUnitOfWork et DbContext:

    public interface IUnitOfWork
{
    IDbSet<TEntity> Set<TEntity>() where TEntity : class;
    int SaveChanges();
}

public class Sample07Context : DbContext, IUnitOfWork
{
    public DbSet<Product> Products { set; get; }

    #region IUnitOfWork Members

    public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
    {
        return base.Set<TEntity>();
    }

    #endregion
}

Logique métier dans les classes de service:

   public interface IProductService
{
    void AddNewProduct(Product product);
    IList<Product> GetAllProducts();
}


    public class ProductService : IProductService
    {
        IUnitOfWork _uow;
        IDbSet<Product> _products;
        public ProductService(IUnitOfWork uow)
        {
            _uow = uow;
            _products = _uow.Set<Product>();
        }
 
        public void AddNewProduct(Product product)
        {
            _products.Add(product);
        }
 
        public IList<Product> GetAllProducts()
        {
            return _products.Include(x => x.Category).ToList();
        }
    }

Injection de la classe de service dans le contrôleur

        public class HomeController : Controller
    {
        private IProductService _productService;
        private IUnitOfWork _uow;

        public HomeController(IUnitOfWork uow, IProductService productService)
        {
            _productService = productService;

            _uow = uow;
        }

        [HttpGet]
        public ActionResult Index()
        {
            var list = _productService.GetAllProducts();
            return View(list);
        }
    }

Configuration StructureMap que nous appelons dans app_start:

       private static void initStructureMap()
        {
            ObjectFactory.Initialize(x =>
            {
                x.For<IUnitOfWork>().HttpContextScoped().Use(() => new Sample07Context());
                x.ForRequestedType<IProductService>().TheDefaultIsConcreteType<EfProductService>();
            });
            //Set current Controller factory as StructureMapControllerFactory
            ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
        }

Tout fonctionne bien avec une seule base de données, mais dans mon scénario, l'utilisateur peut utiliser plusieurs bases de données, je veux dire que l'utilisateur devrait pouvoir modifier la chaîne de connexion au moment de l'exécution. Nous créons une base de données distincte pour chaque projet créé par l'utilisateur dans l'application.
Le problème est maintenant que nous injectons DbContext au service et que DbContext lit la chaîne de connexion à partir de web.config. Ainsi, lorsque l'utilisateur modifie la base de données, nous ne pouvons pas définir de nouvelle chaîne de connexion sur DbContext.
Que suggérez-vous?

18
Shahin

D'après mon expérience, j'ai utilisé le Database First mode dans EF 6. Le DbContext serait généré comme ci-dessous lorsque j'ajoute Entity Data Model.

public TestEntities()
            : base("name=TestEntities")
        {
        }

TestEntities représente l'élément ConnectionString dans App.Config

<connectionStrings>   
<add name="TestEntities" connectionString="..." providerName="System.Data.EntityClient" />
</connectionStrings>

Mais vous pouvez changer le code par défaut ci-dessous.

public partial class TestEntities : DbContext
    {
        public TestEntities()
            : base("name=TestEntities")
        {
        }

        public TestEntities(string sConnectionString)
            : base(sConnectionString)
        {
        }

...}

Vous avez donc deux options pour obtenir une connexion DB.

  1. en utilisant la valeur par défaut. L'EF trouvera la chaîne de connexion dans le fichier de configuration.

  2. en passant la chaîne de connexion à DbContext.

Le code ressemble à ci-dessous.

EntityConnection entityConn =DBConnectionHelper.BuildConnection();
using (var db = new TestEntities(entityConn.ConnectionString))
{
....
}

Quant à la question How to build a EntityConnection?. Veuillez voir MSDN EntityConnection .

J'espère que c'est utile.

Merci.

17
Joe.wang

Par défaut, le nom de la chaîne de connexion à utiliser dans Entity Framework est déduit du nom de votre classe DbContext. Cependant, vous pouvez passez la chaîne de connexion en tant que paramètre constructeur:

public class MyDbContext : DbContext, IUnitOfWork
{
    public MyDbContext(string connectionString)
        : base(connectionString)
    {
    }
}

Ensuite, vous pouvez configurer StructureMap pour passer la chaîne de connexion actuelle, par exemple.

For<IUnitOfWork>().Use(ctx => new MyDbContext(TheConnectionStringToUse));

Cela peut provenir d'une valeur statique que vous définissez dans votre code, de la session en cours, etc.

5
Ben Foster

Je vais suggérer une voie complètement différente. En supposant que vos chaînes de connexion soient configurées dans votre web.config, ce que vous dites, pourquoi n'utilisez-vous pas les transforrms web.debug.config et web.release.config pour définir vos chaînes de connexion de manière appropriée?

c'est-à-dire dans web.debug.config

<connectionStrings>
    <add name="FooEntities" connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string="Data Source=IP,PORT\Instancename;
    Initial Catalog=Foo;Persist Security Info=True;User ID=admin;Password=password;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient"/>
</connectionStrings>

et un web.release.config en tant que tel

<connectionStrings xdt:Transform="Replace">
    <add name="FooEntities" connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string="Data Source=LIVEIP,PORT\Instancename;
    Initial Catalog=Foo;Persist Security Info=True;User ID=admin;Password=password;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient"/>
</connectionStrings>

Une explication très approfondie est disponible ici

0
PlTaylor
 public partial class YourDBContextClass
 {
    // Add a constructor to allow the connection string name to be changed
 public YourDBContextClass(string connectionStringNameInConfig)
        : base("name=" + connectionStringNameInConfig)
    {
    }
}

Ajoutez plusieurs chaînes de connexion à votre fichier Web ou app.config.

dans votre code de programme:

YourDBContextClass dbcontext = new YourDBContextClass("alternateconnectionstringname");
0
Tom