我阅读了很多关于Entity Framework / NHibernate(或基本上任何其他现代ORM)与repository / UnitOfWork模式的使用的内容。显然社区是分裂的。有人会说存储库模式几乎是强制性的,有些人会说它浪费时间...... 好吧,我想出了我自己的"设计,我只是想与你分享,以获得一些反馈......
过去,我的公司决定开发和使用它自己的ORM。它现在已经彻底发生了灾难。性能,稳定性(以及其他一切)都非常糟糕。我们想切换到另一个ORM,我们希望保持从ORM切换到另一个ORM的能力。实际上,我们现在正在使用Sharepoint 2010.它意味着3.5,因此NHibernate 3.4和实体框架4.我们计划尽快迁移到SharePoint 2013,以便能够依赖.net 4.5 / EF 6.1 / ...所以我们将很快就要切换到另一个ORM。
为此,我开发了一组实现" IDatabaseContext"接口。
public interface IDatabaseContext : IDisposable
{
IQueryable<TEntity> AsQueryable<TEntity>()
where TEntity : EntityBase;
IList<TEntity> AsList<TEntity>()
where TEntity : EntityBase;
IList<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> predicate)
where TEntity : EntityBase;
long Count<TEntity>()
where TEntity : EntityBase;
void Add<TEntity>(TEntity entity)
where TEntity : EntityBase;
void Delete<TEntity>(TEntity entity)
where TEntity : EntityBase;
void Update<TEntity>(TEntity entity)
where TEntity : EntityBase;
}
例如,对于原型我决定使用NHibernate:
public class NHibernateDbContext : IDatabaseContext
{
private ISession _session = null;
public NHibernateDbContext(ISessionFactory factory)
{
if (factory == null)
throw new ArgumentNullException("factory");
_session = factory.OpenSession();
}
public IQueryable<TEntity> AsQueryable<TEntity>()
where TEntity : EntityBase
{
return _session.Query<TEntity>();
}
public IList<TEntity> AsList<TEntity>()
where TEntity : EntityBase
{
return _session.QueryOver<TEntity>()
.List<TEntity>();
}
public IList<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> predicate)
where TEntity : EntityBase
{
...
}
public long Count<TEntity>()
where TEntity : EntityBase
{
return _session.QueryOver<TEntity>()
.RowCountInt64();
}
public void Add<TEntity>(TEntity entity)
where TEntity : EntityBase
{
if (entity == null)
throw new ArgumentNullException("entity");
UseTransaction(() => _session.Save(entity));
}
public void Delete<TEntity>(TEntity entity)
where TEntity : EntityBase
{
...
}
public void Update<TEntity>(TEntity entity)
where TEntity : EntityBase
{
...
}
private void UseTransaction(Action action)
{
using (var transaction = _session.BeginTransaction())
{
try
{
action();
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
public void Dispose()
{
if (_session != null)
_session.Dispose();
}
}
最终,我的服务层(每个实体都与服务相关联)依赖于此接口,因此我不介绍对ORM技术的依赖。
public class CountryService<Country> : IService<Country>
where Country : EntityBase
{
private IDatabaseContext _context;
public GenericService(IDatabaseContext context)
{
if (context == null)
throw new ArgumentNullException("context");
_context = context;
}
public IList<Country> GetAll()
{
return _context.AsList<Country>();
}
public IList<Country> Find(Expression<Func<Country, bool>> predicate)
{
return _context.Find(predicate);
}
...
}
最后,要从服务层调用方法,您只需要两行代码:
var service = new CountryService(new NHibernateDbContext(...)));
or
var service = new CountryService(new TestDbContext(...)));
...
我发现这种架构非常简单,使用起来非常方便。我还没有找到任何缺点/缺陷/错误。
那你觉得怎么样?我错过了什么大事吗?有什么我可以改进的吗?
感谢您的反馈......
此致 的Sebastien
答案 0 :(得分:0)
我个人认为你的方法很扎实。但同样我很惊讶你看到这种方法与存储库/工作单元模式非常不同。 您的IService层可直接与基本工作单元层相结合,并与通过接口实现的存储库层相结合。存储库层应该实现一个接口,并由核心层注入或发现,以避免依赖于底层的ORM。
您的存储库和工作单元层的实际实现将特定于基础ORM。但是你可以用RespositoryNH替换RepositoryEF类,反之亦然。 如果正确完成并与依赖注入一起使用,Core应用程序永远不会知道ORM是什么。
问题在于某些人的存储库模式,它们通过允许直接访问ORM或泄漏ORM结构的代码泄漏底层orm。
例如,如果IREPOSITORY从实体框架中暴露了DBSet或Context,那么整个应用程序都可以锁定到EF。
例如工作单元接口
public interface ILuw {
IRepositoryBase<TPoco> GetRepository<TPoco>() where TPoco : BaseObject, new();
void Commit(OperationResult operationResult=null, bool silent=false);
}
和IRepositoryBase接口
public interface IRepositoryBase<TPoco> : IRepositoryCheck<TPoco> where TPoco : BaseObject,new() {
void ShortDump();
object OriginalPropertyValue(TPoco poco, string propertyName);
IList<ObjectPair> GetChanges(object poco, string singlePropName=null);
IQueryable<TPoco> AllQ();
bool Any(Expression<Func<TPoco, bool>> predicate);
int Count();
IQueryable<TPoco> GetListQ(Expression<Func<TPoco, bool>> predicate);
IList<TPoco> GetList(Expression<Func<TPoco, bool>> predicate);
IList<TPoco> GetListOfIds(List<string>ids );
IOrderedQueryable<TPoco> GetSortedList<TSortKey>(Expression<Func<TPoco, bool>> predicate,
Expression<Func<TPoco, TSortKey>> sortBy, bool descending);
IQueryable<TPoco> GetSortedPageList<TSortKey>(Expression<Func<TPoco, bool>> predicate,
Expression<Func<TPoco, TSortKey>> sortByPropertyName,
bool descending,
int skipRecords,
int takeRecords);
TPoco Find(params object[] keyValues);
TPoco Find(string id); // single key in string format, must eb converted to underlying type first.
int DeleteWhere(Expression<Func<TPoco, bool>> predicate);
bool Delete(params object[] keyValues);
TPoco Get(Expression<Func<TPoco, bool>> predicate);
TPoco GetLocalThenDb(Expression<Func<TPoco, bool>> predicate);
IList<TPoco> GetListLocalThenDb(Expression<Func<TPoco, bool>> predicate);
TU GetProjection<TU>(Expression<Func<TPoco, bool>> predicate, Expression<Func<TPoco, TU>> columns);
/// <summary>
/// To use the projection enter an anonymous type like s => new { s.Id , s.UserName});
/// </summary>
IList<TU> GetProjectionList<TU>(Expression<Func<TPoco, bool>> predicate, Expression<Func<TPoco, TU>> columns);
bool Add(object poco,bool withCheck=true);
bool Remove(object poco);
bool Change(object poco, bool withCheck=true);
bool AddOrUpdate(TPoco poco, bool withCheck = true);
}
答案 1 :(得分:0)
您的方法合理,但几乎没有考虑因素
AsList<TEntity>()
方法是一种滥用方法,许多新开发人员可能会使用此方法获取数据列表,并可能会在内存中进行过滤就个人而言,我建议使用phil soady建议的存储库模式,但我不会在IRepository
接口中使用那么多方法。
public interface IRepository<T> where T:IEntity
{
T Single(long id);
T Save(T entity);
void Delete(T entity);
IQueryable<T> FilterBy(Expression<Func<T, bool>> expression);
}
当需要特定类型的查询时,它会在自己的存储库中处理,例如
public interface IContactRepository : IRepository<Contact>
{
IList<Contact> GetForUser(int userId);
}
处理交易时,我会选择使用unit of work per request pattern,您不必在每次更新数据库时手动处理事务。全局ActionFilter就足以实现这一目标。