使用 Entity Framework 的最小存储库实现
Minimal Repository implementation using Entity Framework
我正在尝试在我的应用程序中实现一个最小的通用存储库模式。我有一个非常小的用于查询和保存数据的界面:
public interface IRepository
{
IQueryable<TEntity> Query<TEntity>()
where TEntity: BaseEntity;
void Save<TEntity>(TEntity entity)
where TEntity : BaseEntity;
}
BaseEntity
是我将存储在存储库中的所有对象的基础 class:
public abstract class BaseEntity
{
public Guid Id { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
}
我试图使用 Entity Framework 找到这样一个简单存储库的有效实现,但出乎意料的是很难找到(人们正在使用 UnitOfWork
和其他使实现更复杂的东西比我想要的)。
所以我创建了我能想到的绝对最小的实现:
public class EfRepository : DbContext, IRepository
{
public IQueryable<TEntity> Query<TEntity>() where TEntity : BaseEntity
{
return this.Set<TEntity>();
}
public void Save<TEntity>(TEntity entity) where TEntity : BaseEntity
{
if (entity.Id == default(Guid))
{
entity.Id = Guid.NewGuid();
this.Set<TEntity>().Add(entity);
}
else
{
this.Entry(entity).State = EntityState.Modified;
}
this.SaveChanges();
}
public DbSet<User> Users { get; set; } // User is a subclass of BaseEntity
//Other DbSet's...
}
现在,我的问题是这样的实现是否正确。我问是因为我是 Entity Framework 的新手,我担心在使用此类存储库时可能出现性能问题或可能出错的事情。
注意:我尝试这样做有两个原因:
- 出于测试目的,以便我可以在我的单元测试项目中创建存储库的模拟
- 将来我可能不得不切换到另一个 ORM,我希望这个转换尽可能简单。
首先,存储库是有争议的。由于各种原因,有很多人强烈反对并使用它(或习惯了?)。互联网上有许多文章无休止地讨论利弊。由您决定您的项目中是否真的需要存储库模式 - 让我们不要像您问的那样专注于此 "how to do that in C#?" 而不是 "should i do that?".
您的存储库实现扩展了 DbContext
。这意味着您无法有效地创建跨越多个存储库(多个实体类型)的事务,因为每个存储库都有自己的 DbContext
(因为它是上下文)。在后台 DbContext
跟踪对实体所做的更改。如果您有多个上下文并尝试同时保存它们,它们将不会相互了解。这给我们留下了一个问题——如果 SaveChanges()
的第一次调用成功而第二次失败如何回滚第一个?已经保存了吗?这就是工作单元出现的地方。
所以首先你需要一个存储库接口——充当实体的集合:
public interface IRepository<TEntity>
{
TEntity Get(Expression<Func<TEntity, bool>> predicate);
IEnumerable<TEntity> GetAll();
IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate);
void Add(TEntity entity);
void AddAll(IEnumerable<TEntity> entities);
void Remove(TEntity entity);
void RemoveAll(IEnumerable<TEntity> entities);
}
和工作单元:
public interface IUnitOfWork : IDisposable
{
// Commit all the changes
void Complete();
// Concrete implementation -> IRepository<Foo>
// Add all your repositories here:
IFooRepository Foos {get;}
}
Base 类 可能如下所示:
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected DbContext Context { get; private set; }
public BaseRepository(DbContext dbContext)
{
Context = dbContext;
}
public virtual TEntity Get(Expression<Func<TEntity, bool>> predicate)
{
return Context.Set<TEntity>().Where(predicate).FirstOrDefault();
}
public virtual IEnumerable<TEntity> GetAll()
{
return Context.Set<TEntity>().ToList();
}
public virtual IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate)
{
return Context.Set<TEntity>().Where(predicate).ToList();
}
public void Add(TEntity entity)
{
var entry = Context.Entry(entity);
if(entry.State == EntityState.Detached)
{
Context.Set<TEntity>().Add(entity);
}
else
{
entry.State = EntityState.Modified;
}
}
public void AddAll(IEnumerable<TEntity> entities)
{
foreach(var entity in entities)
{
Add(entity);
}
}
public void Remove(TEntity entity)
{
var entry = Context.Entry(entity);
if (entry.State == EntityState.Detached)
{
Context.Set<TEntity>().Attach(entity);
}
Context.Entry<TEntity>(entity).State = EntityState.Deleted;
}
public void RemoveAll(IEnumerable<TEntity> entities)
{
foreach (var entity in entities)
{
Remove(entity);
}
}
}
和工作单元实现:
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _dbContext;
private IFooRepository _fooRepo;
public UnitOfWork(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
// Each repo will share the db context:
_fooRepo = new FooRepository(_dbContext);
}
public IFooRepository Foos
{
get
{
return _fooRepo;
}
}
public void Complete()
{
_dbContext.SaveChanges();
}
public void Dispose()
{
_dbContext.Dispose();
}
}
我正在尝试在我的应用程序中实现一个最小的通用存储库模式。我有一个非常小的用于查询和保存数据的界面:
public interface IRepository
{
IQueryable<TEntity> Query<TEntity>()
where TEntity: BaseEntity;
void Save<TEntity>(TEntity entity)
where TEntity : BaseEntity;
}
BaseEntity
是我将存储在存储库中的所有对象的基础 class:
public abstract class BaseEntity
{
public Guid Id { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
}
我试图使用 Entity Framework 找到这样一个简单存储库的有效实现,但出乎意料的是很难找到(人们正在使用 UnitOfWork
和其他使实现更复杂的东西比我想要的)。
所以我创建了我能想到的绝对最小的实现:
public class EfRepository : DbContext, IRepository
{
public IQueryable<TEntity> Query<TEntity>() where TEntity : BaseEntity
{
return this.Set<TEntity>();
}
public void Save<TEntity>(TEntity entity) where TEntity : BaseEntity
{
if (entity.Id == default(Guid))
{
entity.Id = Guid.NewGuid();
this.Set<TEntity>().Add(entity);
}
else
{
this.Entry(entity).State = EntityState.Modified;
}
this.SaveChanges();
}
public DbSet<User> Users { get; set; } // User is a subclass of BaseEntity
//Other DbSet's...
}
现在,我的问题是这样的实现是否正确。我问是因为我是 Entity Framework 的新手,我担心在使用此类存储库时可能出现性能问题或可能出错的事情。
注意:我尝试这样做有两个原因:
- 出于测试目的,以便我可以在我的单元测试项目中创建存储库的模拟
- 将来我可能不得不切换到另一个 ORM,我希望这个转换尽可能简单。
首先,存储库是有争议的。由于各种原因,有很多人强烈反对并使用它(或习惯了?)。互联网上有许多文章无休止地讨论利弊。由您决定您的项目中是否真的需要存储库模式 - 让我们不要像您问的那样专注于此 "how to do that in C#?" 而不是 "should i do that?".
您的存储库实现扩展了 DbContext
。这意味着您无法有效地创建跨越多个存储库(多个实体类型)的事务,因为每个存储库都有自己的 DbContext
(因为它是上下文)。在后台 DbContext
跟踪对实体所做的更改。如果您有多个上下文并尝试同时保存它们,它们将不会相互了解。这给我们留下了一个问题——如果 SaveChanges()
的第一次调用成功而第二次失败如何回滚第一个?已经保存了吗?这就是工作单元出现的地方。
所以首先你需要一个存储库接口——充当实体的集合:
public interface IRepository<TEntity>
{
TEntity Get(Expression<Func<TEntity, bool>> predicate);
IEnumerable<TEntity> GetAll();
IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate);
void Add(TEntity entity);
void AddAll(IEnumerable<TEntity> entities);
void Remove(TEntity entity);
void RemoveAll(IEnumerable<TEntity> entities);
}
和工作单元:
public interface IUnitOfWork : IDisposable
{
// Commit all the changes
void Complete();
// Concrete implementation -> IRepository<Foo>
// Add all your repositories here:
IFooRepository Foos {get;}
}
Base 类 可能如下所示:
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected DbContext Context { get; private set; }
public BaseRepository(DbContext dbContext)
{
Context = dbContext;
}
public virtual TEntity Get(Expression<Func<TEntity, bool>> predicate)
{
return Context.Set<TEntity>().Where(predicate).FirstOrDefault();
}
public virtual IEnumerable<TEntity> GetAll()
{
return Context.Set<TEntity>().ToList();
}
public virtual IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate)
{
return Context.Set<TEntity>().Where(predicate).ToList();
}
public void Add(TEntity entity)
{
var entry = Context.Entry(entity);
if(entry.State == EntityState.Detached)
{
Context.Set<TEntity>().Add(entity);
}
else
{
entry.State = EntityState.Modified;
}
}
public void AddAll(IEnumerable<TEntity> entities)
{
foreach(var entity in entities)
{
Add(entity);
}
}
public void Remove(TEntity entity)
{
var entry = Context.Entry(entity);
if (entry.State == EntityState.Detached)
{
Context.Set<TEntity>().Attach(entity);
}
Context.Entry<TEntity>(entity).State = EntityState.Deleted;
}
public void RemoveAll(IEnumerable<TEntity> entities)
{
foreach (var entity in entities)
{
Remove(entity);
}
}
}
和工作单元实现:
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _dbContext;
private IFooRepository _fooRepo;
public UnitOfWork(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
// Each repo will share the db context:
_fooRepo = new FooRepository(_dbContext);
}
public IFooRepository Foos
{
get
{
return _fooRepo;
}
}
public void Complete()
{
_dbContext.SaveChanges();
}
public void Dispose()
{
_dbContext.Dispose();
}
}