Meh*_*taş 2 c# entity-framework repository-pattern
我正在尝试编写一个GenericEFRepository,它将被其他存储库使用.我有一个Save方法如下.
public virtual void Save(T entity) // where T : class, IEntity, new() And IEntity enforces long Id { get; set; }
{
var entry = _dbContext.Entry(entity);
if (entry.State != EntityState.Detached)
return; // context already knows about entity, don't do anything
if (entity.Id < 1)
{
_dbSet.Add(entity);
return;
}
var attachedEntity = _dbSet.Local.SingleOrDefault(e => e.Id == entity.Id);
if (attachedEntity != null)
_dbContext.Entry(attachedEntity).State = EntityState.Detached;
entry.State = EntityState.Modified;
}
Run Code Online (Sandbox Code Playgroud)
您可以在以下代码的评论中找到问题
using (var uow = ObjectFactory.GetInstance<IUnitOfWork>()) // uow is implemented like EFUnitOfWork which gives the DbContext instance to repositories in GetRepository
{
var userRepo = uow.GetRepository<IUserRepository>();
var user = userRepo.Get(1);
user.Name += " Updated";
userRepo.Save(user);
uow.Save(); // OK only the Name of User is Updated
}
using (var uow = ObjectFactory.GetInstance<IUnitOfWork>())
{
var userRepo = uow.GetRepository<IUserRepository>();
var user = new User
{
Id = 1,
Name = "Brand New Name"
};
userRepo.Save(user);
uow.Save();
// NOT OK
// All fields (Name, Surname, BirthDate etc.) in User are updated
// which causes unassigned fields to be cleared on db
}
Run Code Online (Sandbox Code Playgroud)
我能想到的唯一解决方案是通过存储库创建实体,userRepo.CreateEntity(id: 1)并且存储库将返回附加到DbContext的实体.但这似乎容易出错,仍然任何开发人员都可以使用new关键字创建实体.
您对此特定问题的解决方案建议是什么?
注意:我已经知道使用GenericRepository和IEntity接口的缺点和优点.因此,"不要使用GenericRepository,不要使用IEntity,不要在每个实体中放置长ID,不要做你想做的事情"评论也无济于事.
是的,它容易出错,但这只是EF和存储库的问题.您必须先创建实体并在设置要更新的任何数据之前附加它(Name在您的情况下),或者您必须为要保留的每个属性而不是整个实体设置修改状态(正如您可以想象的那样,开发人员可能会忘记做那).
第一个解决方案导致您的存储库执行以下特殊方法:
public T Create(long id) {
T entity = _dbContext.Set<T>().Create();
entity.Id = id;
_dbContext.Set<T>().Attach(entity);
return entity;
}
Run Code Online (Sandbox Code Playgroud)
第二种解决方案需要像
public void Save(T entity, params Expression<Func<T, TProperty>>[] properties) {
...
_dbContext.Set<T>().Attach(entity);
if (properties.Length > 0) {
foreach (var propertyAccessor in properties) {
_dbContext.Entry(entity).Property(propertyAccessor).IsModified = true;
}
} else {
_dbContext.Entry(entity).State = EntityState.Modified;
}
}
Run Code Online (Sandbox Code Playgroud)
你会称之为:
userRepository(user, u => u.Name);
Run Code Online (Sandbox Code Playgroud)