作者:lily0407520 | 来源:互联网 | 2023-05-26 08:22
这是几周前我在这里提出的一个类似的问题,其中一个重要的变化需求.
我有一个新的和独特的(我在stackoverflow搜索中找不到这样的东西)业务要求:
我创建了两个独立的实体框架6 DbContexts,指向两个结构不同的数据库,让我们称它们为PcMaster和PcSubs.虽然PcMaster是一个直接的数据库,而PcMasterContext将有一个静态连接字符串,但PcSubs数据库用作模板来创建新的数据库.显然,由于这些复制的数据库将具有相同的确切结构,因此想法只是在实例化dbcontext时将连接字符串中的数据库(目录)名称更改为指向不同的数据库.我还使用了存储库模式和依赖注入(目前是Ninject,但考虑转向Autofac).
我没有看到DbContext的IDbContext接口,除非你想自己创建一个.但后来,我看到很多人说这不是一个好主意或不是最好的做法.
基本上,我想要做的是,在某些条件下,不仅应用程序需要在PCMasterContext和PCSubsContext之间切换,而且如果PCSubsContext是当前上下文,还要将连接字符串修改为PCSubsContext.我在存储库中使用的dbContext需要指向不同的数据库.我不知道如何使用像Ninject或Autofac这样的IoC容器来做到这一点.这是我到目前为止创建的一些代码片段.非常感谢帮助一些真正的工作解决方案.
这是我的基础存储库的界面
public interface IPCRepositoryBase where T : class
{
void Add(T entity);
void Delete(T entity);
T FindOne(Expression> predicate);
IQueryable FindBy(Expression> predicate);
IQueryable GetAll();
void SetConnection(string connString);
//...
//...
}
这是我的抽象存储库库
public abstract class PCRepositoryBase : IPCRepositoryBase, IDisposable where T : class
{
protected readonly IDbSet dbSet;
protected DbContext dbCtx;
public PCRepositoryBase(DbContext dbCtx)
{
this.dbCtx = dbCtx;
dbSet = dbCtx.Set();
}
public void SetConnection(string connString)
{
dbCtx.Database.Connection.COnnectionString= connString;
}
public IQueryable FindBy(Expression> predicate)
{
return dbSet.Where(predicate); // DataContext.Set().Where( predicate );
}
public virtual IQueryable GetAll()
{
return dbSet;
}
public T FindOne(Expression> predicate)
{
return dbSet.SingleOrDefault(predicate);
}
//... Not all implementations listed
//...
}
现在,这是一个派生存储库的接口:
public interface ISubscriberRepository : IPCRepositoryBase
{
IQueryable GetByStatusName( PCEnums.enumSubsStatusTypes status );
IQueryable GetByBusinessName( string businessName );
//...
//...
}
public class SubscriberRepository : PCRepositoryBase, ISubscriberRepository
{
public SubscriberRepository( DbContext context ) : base( context ) { }
public IQueryable GetByStatusName( PCEnums.enumSubsStatusTypes status )
{
return FindBy(x => x.SubsStatusType.Name == status.ToString());
}
public IQueryable GetByBusinessName( string businessName )
{
return FindBy( s => s.BusinessName.ToUpper() == businessName.ToUpper() );
}
//... other operations omitted for brevity!
}
现在,我的PCSubs dbContext由设计师生成:
public partial class PCSubsDBContext : DbContext
{
public PCSubsDBContext() : base("name=PCSubsDBContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet Currencies { get; set; }
public virtual DbSet DurationNames { get; set; }
public virtual DbSet Subscribers { get; set; }
}
有没有办法,我可以为两个数据库使用和/或注入一个通用dbcontext以及不同数据库的连接字符串.如何在没有相应接口的情况下在Ioc容器中注册"DbContext",并且仍然能够在运行时注入连接字符串?一些代码示例将真正有所帮助.
1> Steven..:
解决方案实际上非常简单.您需要确保您PCSubsDBContext
的构造函数包含连接字符串,连接字符串名称,数据库名称或类似名称.这样您就可以PCSubsDBContext
根据它所处的上下文创建适当的值.注入其ctor的值可能取决于登录用户或某个请求.这是你已经知道该怎么做的事情.
如何注册取决于您的容器,但您通常必须为此注册一个委托.这可能如下所示:
// Autofac
builder.Register(c =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest());
// Ninject
kernel.Bind().ToMethod(m =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest());
// Simple Injector
container.Register(() =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest());
由于创建上下文取决于请求的可用性,因此可能会稍微更改您的设计,以便PCSubsDBContext
可以懒惰地加载,而您仍然可以在不存在Web请求的情况下构建对象图的其余部分.这非常有价值,因为这可以让您验证容器的配置.
解决方案(一如既往)是引入一种新的抽象,例如:
public interface IPcSubsContextProvider
{
PCSubsDBContext Context { get; }
}
现在,PCSubsDBContext
您可以在执行期间注入IPcSubsContextProvider
并使用其Context
属性(而不是在构建对象图时),而不是直接向消费者注入.这允许PCSubsDBContext
仅在需要时才创建,并且仅在构建了对象图的其余部分之后才创建.实施将是微不足道的:
class LazyPcSubsContextProvider : IPcSubsContextProvider
{
private readonly Lazy context;
public LazyPcSubsContextProvider(Func factory) {
this.cOntext= new Lazy(factory);
}
public PCSubsDBContext Context { get { return this.context.Value; } }
}
可以使用作用域/请求生活方式注册此实现:
// Autofac
builder.Register(c =>
new LazyPcSubsContextProvider(() =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest())))
.InstancePerHttpRequest();
// Ninject
kernel.Bind().ToMethod(m =>
new LazyPcSubsContextProvider(() =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest())))
.InRequestScope();
// Simple Injector
container.RegisterPerWebRequest(() =>
new LazyPcSubsContextProvider(() =>
new PCSubsDBContext(GetConnectionStringForCurrentRequest())));
在此之后,Simple Injector将使验证配置变得非常容易:
container.Verify();
它还允许您诊断配置.
使用其他容器,这将更难做到.