在EF中跟踪更新带有子集合的实体感到困惑

3

我是一名新手,对EF(我使用的是EF6)不太理解,我遇到了更新包含子集合的实体的问题。

这是我的实体类:

public class TimeSheet
{
    public int TimeSheetID { get; set; }
    public virtual ICollection<TimeSheetDetail> Details { get; set; }
}

public class TimeSheetDetail
{
    public int TimeSheetDetailID { get; set; }
    public int TimeSheetID { get; set; }
    public virtual TimeSheet TimeSheet { get; set; }
}

我的更新方法:

public void Update(TimeSheet obj)
{
    var objFromDB = Get(obj.TimeSheetID);
    var deletedDetails = objFromDB.Details.Except(obj.Details).ToList();

    _dbContext.Entry(obj).State = EntityState.Modified;

    //track if details exist
    foreach (var details in obj.Details)
    {
        _dbContext.Entry(details).State = details.TimeSheetDetailID == 0 ? EntityState.Added : EntityState.Modified;
    }

    //track deleted item
    foreach (var deleted in deletedDetails)
    {
        _dbContext.Entry(deleted).State = EntityState.Deleted;
    }
}

public TimeSheet Get(object id)
{
    //return _timeSheet.Find(id); //Without AsNoTracking I got error
    int x = Convert.ToInt32(id);
    return _timeSheet.AsNoTracking().SingleOrDefault(a => a.TimeSheetID == x);
}

以上代码给出了Attaching an entity of type 'ClassName' failed because another entity of the same type already has the same primary key value。所以我的问题是:

  1. 如何使用EF更新子集合?即如果在POST中不存在于数据库中,则需要添加新的,否则进行更新,或者如果它已从POST中删除,则从数据库中删除。

  2. 如果我不使用AsNoTracking(),它会抛出Saving or accepting changes failed because more than one entity of type 'ClassName' have the same primary key value。我注意到错误是由于我的DbSet将数据从数据库添加到其Local属性中引起的,如果我不使用AsNoTracking(),这会导致EF框架抛出错误,因为它认为我有重复数据。这实际上是如何工作的?

  3. 正如您所看到的,我正在尝试将objFromDbobj进行比较,以检查用户是否删除了其中一个详细信息,以便我可以将其从数据库中删除。相反,我从集合结果中得到了一堆DynamicProxies。什么是DynamicProxies,它是如何工作的?

  4. 有关EF的任何好文章或101教程吗?到目前为止,我只看到了一个简单的教程,对我的情况没有帮助,我已经四处寻找,并找到了关于如何处理的混合答案。说实话,在这一点上,我希望我只是使用经典的ADO.Net而不是EF。

1个回答

3
为了更好地理解实体框架,可以将 DbContext 视为应用程序和数据库之间的代理。 DbContext 将缓存所有内容,并使用缓存值中的每个数据位,除非您告诉它不要这样做。
对于第一点:这取决于您的环境,如果在选择和更新实体之间未处理 DbContext,则只需调用 SaveChanges 即可保存数据。 如果已处理 DbContext,则可以从上下文中分离实体,更改数据,重新附加它们并将 EntityState 设置为修改。
我无法给出100%确定的答案,因为我大约半年前停止使用实体框架。 但是我知道更新复杂关系很麻烦。

对于2.: 命令AsNoTracking告诉EF不要跟踪此查询中实体所做的更改。例如,从数据库中选择5个TimeSheets,更改第一个实体中的某些值并删除最后一个实体。如果您调用SaveChangesDbContext将自动更新第一个实体,删除最后一个实体,并保留其他实体不变。现在您尝试自己更新实体并再次将第一个实体附加到DbContext。

DbContext现在将具有相同键的两个实体,这会导致异常。

对于3.: DynamicProxies是实体框架用于跟踪这些实体更改的对象。

对于4.: 请查看link,还有一本关于实体框架6的好书(标题:“Programming Entity Framework”)


1
对于第二个问题,如果EF可以自动跟踪实体内部的更改,为什么它不能检测到子集合内部的更改呢?例如,如果我在我的更新方法中使用以下代码“_timeSheet.Attach(obj); _dbContext.Entry(obj).State = EntityState.Modified;”,它将无法识别集合属性内部的更改。 - tickwave
实体框架无法跟踪导航属性内部的更改。导航属性可能是通过延迟加载加载的,也可能为空,如果使用空集合更新实体,则实体框架不知道该怎么处理。 - JoeJoe87577

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接