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?
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.
en utilisant la valeur par défaut. L'EF trouvera la chaîne de connexion dans le fichier de configuration.
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.
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.
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
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");