在添加实体时,我遇到了使用Generic Repository和Entity Framework的问题。这是repo界面:
public interface IRepository<TEntity, in TKey> where TEntity : class
{
IQueryable<TEntity> GetQueryable();
IEnumerable<TEntity> GetAll();
IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate);
TEntity First(Expression<Func<TEntity, bool>> predicate);
TEntity Single(Expression<Func<TEntity, bool>> predicate);
TEntity SingleOrDefault(Expression<Func<TEntity, bool>> predicate);
void Add(TEntity entity);
void Attach(TEntity entity);
void Delete(TEntity entity);
}
我的EfRepository实现:
public class EFRepository<TEntity,TKey> : IRepository<TEntity,TKey> where TEntity : class
{
private readonly DbSet<TEntity> _dbSet;
private readonly DbContext _context;
public EFRepository(DbSet<TEntity> dbSet,DbContext context)
{
_dbSet = dbSet;
_context = context;
}
public IQueryable<TEntity> GetQueryable()
{
return _dbSet.AsQueryable();
}
public IEnumerable<TEntity> GetAll()
{
return _dbSet;
}
public IQueryable<TEntity> Find(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
{
return _dbSet.Where(predicate);
}
public TEntity FirstOrDefault(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
{
return _dbSet.FirstOrDefault(predicate);
}
public TEntity First(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
{
return _dbSet.First(predicate);
}
public TEntity Single(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
{
return _dbSet.Single(predicate);
}
public TEntity SingleOrDefault(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
{
return _dbSet.SingleOrDefault(predicate);
}
public void Add(TEntity entity)
{
_dbSet.Add(entity);
}
public void Attach(TEntity entity)
{
_dbSet.Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
public void Delete(TEntity entity)
{
_context.Set<TEntity>().Attach(entity);
_context.Entry(entity).State = EntityState.Deleted;
}
如果我的DbSet与其他表没有任何关系,那么这很好。但是请注意以下几点:
class TopType
{
public TopType()
{
InnerTypes = new HashSet<InnerType>();
}
public int id { get; set;}
public string something { get; set;}
public virtual ICollection<InnerType> InnerTypes { get; set;}
}
class InnerType
{
public InnerType()
{
ChildTypes = new HashSet<ChildType>();
}
public int id { get; set;}
public nullable<int> TopTypeId { get; set;}
public string somethingElse { get; set;}
public virtual TopType { get; set;}
public virtual ICollection<ChildType> ChildTypes { get; set;}
}
class ChildType
{
public ChildType()
{
InnerTypes = new HashSet<InnerType>();
}
public int id { get; set;}
public string somethingForTheChild { get; set;}
public virtual ICollection<InnerType> InnerTypes { get; set;}
}
我在db中已经有了一些子类型。这些将返回给Web客户端。 Web客户端的用户创建新的TopType,然后添加尽可能多的InnerType。对于每个内部类型,他们可以选择已存在于数据库中并具有id等填充的子类型。客户端代码正确设置对象并且在所有类型上实现了导航属性。
在服务层上,我有一个工作单元,如下所示:
public class WorkoutUnitOfWork : IWorkoutUnitOfWork
{
private WorkoutEntities entities;
public WorkoutUnitOfWork()
{
entities = new WorkoutEntities();
entities.Configuration.ProxyCreationEnabled = false;
}
private IRepository<TopType, int> topTypeRepository;
private IRepository<InnerType, int> innerTypeRepository;
private IRepository<ChildType, int> childTypeRepository;
public IRepository<Workout, int> TopTypeRepository
{
get { return topTypeRepository ?? new EFRepository<TopType, int>(entities.TopTypes, entities); }
}
public IRepository<InnerType, int> InnerTypeRepository
{
get { return innerTypeRepository ?? new EFRepository<InnerType, int>(entities.InnerTypes, entities); }
}
public IRepository<ChildType, int> ChildTypeRepository
{
get { return childTypeRepository ?? new EFRepository<ChildType,int>(entities.ChildTypes, entities); }
}
public void Commit()
{
entities.SaveChanges();
}
当我通过在存储库中调用Add的填充TopType时,TopType会像InnerTypes一样添加到数据库中。这些都是插入物。还会插入子类型。我知道这是因为Add方法设置要添加的所有实体状态。我也知道我正在为每个请求使用新的上下文。我的数据访问位于服务的单独项目中,该服务使用它来保持持久性。我知道我需要控制子类型的状态来告诉它们已经存在的上下文。我的问题是使用Generic Repository模式这可能吗?是否有某种方法可以搜索所有子集合并获取/测试其状态。
感觉好像我应该更多地做一些manaually,例如在没有设置任何导航属性的情况下添加TopType,然后使用返回的Id在InnerType上设置外键,然后保存它以创建必要的条目连接表。
如果这是唯一合理的方法,那么UnitOfWOrk将不得不停止提供回购并开始控制对Reposiotry类的访问。
关于已知工作的任何建议都会很棒。如果我可以帮助它,我不想退回到命名的存储库实现。
答案 0 :(得分:1)
对我有用的答案是正确使用外键,而不是在添加相关实体时填充导航属性。当我移动到设置外键而不是导航属性时,所有操作都按预期工作。这是因为导航属性被视为新条目,即使它已存在于数据库中。这是我的方法的产物,但对我来说似乎是一个公平的妥协。
答案 1 :(得分:0)
在断开连接的存储库方案中,可以使用重载的泛型插入方法,并将子对象设置为不变。例如
在IGenericRepository接口中
Task<T> Insert(T entity, object[] childEntities);
在GenericRepository中
public async Task<T> Insert(T entity, object[] childEntities)
{
try
{
using (EntityContext_context = new EntityContext())
{
foreach (var item in childEntities)
{
_context.Entry(item).State = EntityState.Unchanged;
}
_context.Entry(entity).State = EntityState.Added;
await _context.SaveChangesAsync();
return entity;
}
}
catch (Exception)
{
throw;
}
}
在实际方法中
await _uow.EntityRepository.Insert(_ParentEntity, new object[] { _ParentEntity.ChildEntity1,_ParentEntity.ChildEntity2 });
在上述任务中将实体正确转换为对象数组时,父实体的子实体将不会重复。