CastleWindsor、Generic Repository 和两个数据库上下文
CastleWindsor, Generic Repository and two database contexts
我有两个单独的数据库用于存储文档和用户。我还实现了通用存储库模式:
public class Repository<T> : IRepository<T> where T : class
{
public DbContext Context { get; set; }
public Repository()
{
}
public IEnumerable<T> Get(Expression<Func<T, bool>> expression)
{
return Context.Set<T>().Where(expression).AsEnumerable();
}
public void Add(T entity)
{
Context.Set<T>().Add(entity);
}
public void Delete(T entity)
{
Context.Set<T>().Remove(entity);
}
public void Update(T entity)
{
Context.Set<T>().Attach(entity);
Context.Entry<T>(entity).State = EntityState.Modified;
}
public void SaveChanges()
{
Context.SaveChanges();
}
}
问题是实体存储在不同的 DbContext 中,我不能使用这样的东西:
container.Register(Component.For(typeof(IRepository<>)).ImplementedBy(typeof(Repository<>));
如何指定每个实体应使用哪个 DbContext?
例如,如果我想要创建存储库,这意味着应该使用一个数据库,但如果我想要存储库,则应该使用另一个上下文。
或者我应该创建两个 repo 类,如下所示:
public class AttachmetRepository<T> : IRepository<T> where T : class
{
public AttachmetsDbContext Context { get; set; }
...
}
public class UserRepository<T> : IRepository<T> where T : class
{
public UsersDbContext Context { get; set; }
...
}
我不想使用两个不同的存储库的原因是为了保持服务简单,像这样:
public class SomeService: ISomeService
{
public IRepository<User> UserRepository { get; set; } //database 1
public IRepository<Comment> CommentsRepository { get; set; } //database 1
public IRepository<Attachment> AttachmentRepository { get; set; } //database 2
...
}
更新:
正如 Ognyan 所建议的那样,我使用了 FactoryMethod,这很有帮助!非常感谢,Ognyan!
我是 CastleWindsor 的新手,我不确定它是最好最快的方法,但这是我的代码:
public class EFDatabaseInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<AttContext>().LifeStyle.PerWebRequest);
container.Register(Component.For<DefContext>().LifeStyle.PerWebRequest);
container.Register(Component.For(typeof(IRepository<>)).UsingFactoryMethod((kernel, context) =>
{
var genericType = context.RequestedType.GetGenericArguments()[0];
Type type = typeof(Repository<>).MakeGenericType(genericType);
object repository = Activator.CreateInstance(type);
PropertyInfo dbContextProperty = type.GetProperty("Context");
if (genericType == typeof(Attachment))
{
dbContextProperty.SetValue(repository, kernel.Resolve<AttContext>());
}
else
{
dbContextProperty.SetValue(repository, kernel.Resolve<DefContext>());
}
return repository;
}).LifeStyle.PerWebRequest);
}
}
首先,您不需要在存储库中对 DbContext 进行硬编码。您可以像这样重新制作您的存储库:
public class Repository<T> : IRepository<T> where T : class
{
private readonly DbContext _dbContext;
// you can even make it IDbContextProvider with .Current() method in order not
// to place a hard dependency but depend on Interface which is the proper way.
// I was in a hurry and did not want to overcomplicate the implementation.
public Repository(DbContext dbContext)
{
_dbContext = dbContext;
}
protected IDbSet<T> CreateSet<T>() where T : class
{
return _dbContext.Set<T>();
}
public virtual T Find(int id)
{
return CreateSet<T>().Find(id);
}
...
}
之后你需要一个工厂方法和一种区分目标数据库的方法。一种区分方法是从工厂方法的 CreationContext 中获取信息:
private static DbContext DbContextFactoryMethod(IKernel k, ComponentModel cm, CreationContext c)
在这里您可以遍历解析堆栈,看看这是否是包含 IRepository 或其他实体的图形的一部分,然后选择您的数据库。
通过这种方式,您将在存储库中获得正确的 DbContext,而无需将它们全部粘在里面,这会随着时间的推移变得越来越麻烦。
我有两个单独的数据库用于存储文档和用户。我还实现了通用存储库模式:
public class Repository<T> : IRepository<T> where T : class
{
public DbContext Context { get; set; }
public Repository()
{
}
public IEnumerable<T> Get(Expression<Func<T, bool>> expression)
{
return Context.Set<T>().Where(expression).AsEnumerable();
}
public void Add(T entity)
{
Context.Set<T>().Add(entity);
}
public void Delete(T entity)
{
Context.Set<T>().Remove(entity);
}
public void Update(T entity)
{
Context.Set<T>().Attach(entity);
Context.Entry<T>(entity).State = EntityState.Modified;
}
public void SaveChanges()
{
Context.SaveChanges();
}
}
问题是实体存储在不同的 DbContext 中,我不能使用这样的东西:
container.Register(Component.For(typeof(IRepository<>)).ImplementedBy(typeof(Repository<>));
如何指定每个实体应使用哪个 DbContext?
例如,如果我想要创建存储库,这意味着应该使用一个数据库,但如果我想要存储库,则应该使用另一个上下文。
或者我应该创建两个 repo 类,如下所示:
public class AttachmetRepository<T> : IRepository<T> where T : class
{
public AttachmetsDbContext Context { get; set; }
...
}
public class UserRepository<T> : IRepository<T> where T : class
{
public UsersDbContext Context { get; set; }
...
}
我不想使用两个不同的存储库的原因是为了保持服务简单,像这样:
public class SomeService: ISomeService
{
public IRepository<User> UserRepository { get; set; } //database 1
public IRepository<Comment> CommentsRepository { get; set; } //database 1
public IRepository<Attachment> AttachmentRepository { get; set; } //database 2
...
}
更新: 正如 Ognyan 所建议的那样,我使用了 FactoryMethod,这很有帮助!非常感谢,Ognyan! 我是 CastleWindsor 的新手,我不确定它是最好最快的方法,但这是我的代码:
public class EFDatabaseInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<AttContext>().LifeStyle.PerWebRequest);
container.Register(Component.For<DefContext>().LifeStyle.PerWebRequest);
container.Register(Component.For(typeof(IRepository<>)).UsingFactoryMethod((kernel, context) =>
{
var genericType = context.RequestedType.GetGenericArguments()[0];
Type type = typeof(Repository<>).MakeGenericType(genericType);
object repository = Activator.CreateInstance(type);
PropertyInfo dbContextProperty = type.GetProperty("Context");
if (genericType == typeof(Attachment))
{
dbContextProperty.SetValue(repository, kernel.Resolve<AttContext>());
}
else
{
dbContextProperty.SetValue(repository, kernel.Resolve<DefContext>());
}
return repository;
}).LifeStyle.PerWebRequest);
}
}
首先,您不需要在存储库中对 DbContext 进行硬编码。您可以像这样重新制作您的存储库:
public class Repository<T> : IRepository<T> where T : class
{
private readonly DbContext _dbContext;
// you can even make it IDbContextProvider with .Current() method in order not
// to place a hard dependency but depend on Interface which is the proper way.
// I was in a hurry and did not want to overcomplicate the implementation.
public Repository(DbContext dbContext)
{
_dbContext = dbContext;
}
protected IDbSet<T> CreateSet<T>() where T : class
{
return _dbContext.Set<T>();
}
public virtual T Find(int id)
{
return CreateSet<T>().Find(id);
}
...
}
之后你需要一个工厂方法和一种区分目标数据库的方法。一种区分方法是从工厂方法的 CreationContext 中获取信息:
private static DbContext DbContextFactoryMethod(IKernel k, ComponentModel cm, CreationContext c)
在这里您可以遍历解析堆栈,看看这是否是包含 IRepository 或其他实体的图形的一部分,然后选择您的数据库。
通过这种方式,您将在存储库中获得正确的 DbContext,而无需将它们全部粘在里面,这会随着时间的推移变得越来越麻烦。