Entity framework 实体框架:如何使用通用存储库模式删除子项?

Entity framework 实体框架:如何使用通用存储库模式删除子项?,entity-framework,entity-framework-6,repository-pattern,Entity Framework,Entity Framework 6,Repository Pattern,这个问题已经在stackoverflow上讨论了好几次,但是我找不到关于如何使用通用存储库模式解决这个问题的答案。 给出的所有答案都直接使用DBContext。在通用存储库模式中,我将不能直接访问DBContext,而且我使用UnityforIOC 问题是:我有父对象,父对象有子集合。我正在设置父对象的一些属性,并从集合中删除子对象。然而,当我调用SaveChanges()时,我得到一个错误 操作失败:无法更改关系,因为 一个或多个外键属性不可为空。当 对关系进行更改时,相关的外键属性为 设置为

这个问题已经在stackoverflow上讨论了好几次,但是我找不到关于如何使用通用存储库模式解决这个问题的答案。 给出的所有答案都直接使用DBContext。在通用存储库模式中,我将不能直接访问DBContext,而且我使用UnityforIOC

问题是:我有父对象,父对象有子集合。我正在设置父对象的一些属性,并从集合中删除子对象。然而,当我调用
SaveChanges()
时,我得到一个错误

操作失败:无法更改关系,因为 一个或多个外键属性不可为空。当 对关系进行更改时,相关的外键属性为 设置为空值。如果外键不支持空值, 必须定义新关系,外键属性必须为 指定了另一个非空值,或者必须为不相关的对象 删除

现在我不知道为什么EF试图将FK设置为null,而不是仅仅删除记录。将FK设置为null,但将孤立记录保留在DB中的目的是什么

如何使用存储库模式解决此问题?我是否需要从存储库中公开任何新方法

实体

    public class parent
    {
        public int ParentID {get;set;}  //Primary Key

        public string ParentName {get;set}

        public ICollection<Child> Children {get;set}
    }

    public class Child
    {
        public int ChildID {get;set;}  //Primary Key

        public string ChildName {get;set;}

        public int ParentID {get;set;}  //Foreign Key
    }
    public class MyService
    {
        private IGenericRepository _repository;

        public MyService(IGenericRepository repository)
        {
          _repository = repository;
        }

        public void UpdateParent(int parentID,string parentName, int[] sourceChildIDs)
        {
            var p = _repository.GetQuery<Parent>()
                .Include(x => x.Children)
                .Where(x => x.ParentID == parentID)
                .SingleOrDefault();

            p.ParentName = parentName;

            var childrenToDetete = new List<Child>();
            foreach (var child in p.Children)
            {
                if (!sourceChildIDs.Contains(child.ChildID))
                {
                    childrenToDetete.Add(child);
                }
            }

            foreach (var child in childrenToDetete)
            {
                p.Children.Remove(child);
            }            

            _repository.SaveChanges(); // i get error here
        }
    }
公共类父类
{
public int ParentID{get;set;}//主键
公共字符串ParentName{get;set}
公共ICollection子项{get;set}
}
公营儿童
{
public int ChildID{get;set;}//主键
公共字符串ChildName{get;set;}
public int ParentID{get;set;}//外键
}
服务

    public class parent
    {
        public int ParentID {get;set;}  //Primary Key

        public string ParentName {get;set}

        public ICollection<Child> Children {get;set}
    }

    public class Child
    {
        public int ChildID {get;set;}  //Primary Key

        public string ChildName {get;set;}

        public int ParentID {get;set;}  //Foreign Key
    }
    public class MyService
    {
        private IGenericRepository _repository;

        public MyService(IGenericRepository repository)
        {
          _repository = repository;
        }

        public void UpdateParent(int parentID,string parentName, int[] sourceChildIDs)
        {
            var p = _repository.GetQuery<Parent>()
                .Include(x => x.Children)
                .Where(x => x.ParentID == parentID)
                .SingleOrDefault();

            p.ParentName = parentName;

            var childrenToDetete = new List<Child>();
            foreach (var child in p.Children)
            {
                if (!sourceChildIDs.Contains(child.ChildID))
                {
                    childrenToDetete.Add(child);
                }
            }

            foreach (var child in childrenToDetete)
            {
                p.Children.Remove(child);
            }            

            _repository.SaveChanges(); // i get error here
        }
    }
公共类MyService
{
私有IGenericRepository_存储库;
公共MyService(IGenericRepository存储库)
{
_存储库=存储库;
}
public void UpdateParent(int parentID、string parentName、int[]sourcechildId)
{
var p=\u repository.GetQuery()
.包括(x=>x.儿童)
.Where(x=>x.ParentID==ParentID)
.SingleOrDefault();
p、 ParentName=ParentName;
var childrenToDetete=新列表();
foreach(p.Children中的var child)
{
如果(!sourcechildid.Contains(child.ChildID))
{
childrenToDetete.Add(child);
}
}
foreach(childrenToDetete中的var child)
{
p、 儿童。移除(儿童);
}            
_repository.SaveChanges();//这里出现错误
}
}
存储库

    public class GenericRepository : IGenericRepository
    {

        private DbContext _dbContext;        


        public GenericRepository(DbContext dbContext)
        {
            if (dbContext == null)
            {
                throw new ArgumentNullException("dbContext");
            }

            _dbContext = dbContext;
        }


        public TEntity Create<TEntity>() where TEntity : class
        {
            return _dbContext.Set<TEntity>().Create<TEntity>();
        }

        public TEntity Add<TEntity>(TEntity entity) where TEntity : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }

            return _dbContext.Set<TEntity>().Add(entity);
        }

        public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
        {
            return _dbContext.Set<TEntity>();
        }

        public IQueryable<TEntity> GetQuery<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
        {
            return GetQuery<TEntity>().Where(predicate);
        }    

        public void Delete<TEntity>(TEntity entity) where TEntity : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }

            _dbContext.Set<TEntity>().Remove(entity);
        }

        public void Delete<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class
        {
            IEnumerable<TEntity> records = GetQuery<TEntity>(criteria);

            foreach (TEntity record in records)
            {
                Delete<TEntity>(record);
            }
        }

        public void Update<TEntity>(TEntity entity) where TEntity : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }

            _dbContext.Entry(entity).State = EntityState.Modified;
        }

        public int SaveChanges()
        {
            return _dbContext.SaveChanges();
        }       
    }
公共类GenericRepository:IGenericRepository
{
私有DbContext _DbContext;
公共通用存储库(DbContext DbContext)
{
if(dbContext==null)
{
抛出新ArgumentNullException(“dbContext”);
}
_dbContext=dbContext;
}
public tenty Create(),其中tenty:class
{
返回_dbContext.Set().Create();
}
公共TEntity添加(TEntity实体),其中TEntity:类
{
if(实体==null)
{
抛出新的ArgumentNullException(“实体”);
}
返回_dbContext.Set().Add(实体);
}
public IQueryable GetQuery(),其中tenty:class
{
返回_dbContext.Set();
}
公共IQueryable GetQuery(表达式谓词),其中tenty:class
{
返回GetQuery().Where(谓词);
}    
公共void Delete(tenty实体),其中tenty:class
{
if(实体==null)
{
抛出新的ArgumentNullException(“实体”);
}
_dbContext.Set().Remove(实体);
}
public void Delete(表达式条件),其中tenty:class
{
IEnumerable记录=GetQuery(条件);
foreach(记录中的tenty记录)
{
删除(记录);
}
}
公共无效更新(TEntity实体),其中TEntity:类
{
if(实体==null)
{
抛出新的ArgumentNullException(“实体”);
}
_dbContext.Entry(entity.State=EntityState.Modified;
}
公共int SaveChanges()
{
返回_dbContext.SaveChanges();
}       
}

您正在从父对象中删除子对象,而不是从数据库中删除。作为旁注,您可以用一种更简洁的方式:

foreach (var child in p.Children
                       .Where(child => !sourceChildIDs.Contains(child.ChildID))
                       .ToList())
{
    p.Children.Remove(child);
}
但这只会打破父母和孩子之间的联系。EF的错误在于谨慎,并假定您只想删除子项的外键引用,而不是完全删除子项

因此,您必须通过将以前的语句替换为从数据库中删除子语句

var delIds = p.Children.Where(child => !sourceChildIDs.Contains(child.ChildID))
                       .Select(c => c.ChildID).ToList();
_repository.Delete<Child>(c => delIds.Contains(c.ChildID));
var delIds=p.Children.Where(child=>!sourceChildIDs.Contains(child.ChildID))
.Select(c=>c.ChildID).ToList();
_Delete(c=>delIds.Contains(c.ChildID));

顺便说一句,这是一个相当少见的通用存储库实现。通常,泛型存储库针对一种类型进行实例化,即定义为
GenericRepository
。这些存储库的实例通常共享一个上下文实例,同时它们在一个工作单元中进行协作,该工作单元也保存了更改。

您正在从父项中删除子项,但不是从数据库中删除子项。作为旁注,您可以用一种更简洁的方式:

foreach (var child in p.Children
                       .Where(child => !sourceChildIDs.Contains(child.ChildID))
                       .ToList())
{
    p.Children.Remove(child);
}
但这只会打破父母和孩子之间的联系。EF在谨慎方面出错,并假定您只想删除子项的外键引用