实体框架背景不按我的预期行事

时间:2015-02-25 21:30:23

标签: entity-framework transactions dbcontext

我在实体框架中看到一些让我觉得我完全误解了数据库环境是如何工作的,或者EF实际上已经破坏了(我知道这很可能意味着我只是没有得到它)​​。< / p>

考虑以下情况:

在数据库中,我有一堆学生出勤记录,他们都有一个标记为P的代码。

然后我们有类似于以下内容的东西:

public void SetAttendance(int dayId,int attendanceId, int attendanceId, String mark)
{ 
    updateAttendance = new StudentAttendance()
    {
     Code=String.Empty,
     AttendanceId=attendanceId,
     DayId = dayId
    };
    context.Attach(updateAttendance);
//I don't save changes yet because I now need to do some logic
var markedAttendanceCount = context.StudentAttendance.Where(att=> !String.IsNullOrEmpty(att.Code) && att.DayId == dayId).Count();

var allAttendanceCount = context.StudentAttendance.Where(att=> att.DayId == dayId).Count();

    var updateDay = new ClassDay() 
    {
       DayId = dayId,
       AllMarked = markedAttendanceCount = allAttendanceCount
     };
  context.Attach(updateDay);
  context.SaveChanges();
}

我希望如果我按如下方式调用SetAttendance方法:

myworker.SetAttendance(10,20,String.Empty);

它应该正确地认识到当天的出勤没有完全标记。我反而看到的是我对Context.StudentAttendance的查询int查询是在询问数据库。因此,我对当天状态变化的检测始终是落后的。

我认为上下文基本上应该足够聪明,让你写这样的东西。从本质上讲,我一直认为db上下文允许您基本上使用可序列化的事务类型的行为。当您通过上下文对数据进行更改时,这些更改将反映在针对上下文的查询中。我错过了什么吗?

2 个答案:

答案 0 :(得分:0)

EF不会这样,不。基本上是出于性能原因。

但是,context.StudentAttendance.Local公开包含添加的实体的集合。它还包含先前由查询中的上下文加载的任何实体。如果您愿意,可以按照此处所述将整个DbSet加载到内存中:https://msdn.microsoft.com/en-au/data/jj592872.aspx但是这不适用于大型数据集。

此外,您应该使用context.Add()而不是context.Attach()。后者适用于已存在于数据库中的实体。

答案 1 :(得分:0)

你确实不完全理解EF是如何工作的,但我认为EF应该归咎于此。你问的是一个非常好的有效问题。

与EF相比,NHibernate有AutoFlush feature。这意味着“在任何时候”它可以提交对数据库的更改,以便在没有开发人员干预的情况下保持本地更改和数据库内容同步。在您的情况下,它会在从数据库查询现有的StudentAttendance之前保存新的DayId == dayId。因此,如果新谓词与谓词(allAttendanceCount)匹配,则它会为StudentAttendance做出贡献。如果理解得当,这个功能可以非常直观地处理数据,完全符合您预期EF的行为方式。

然而,事实是,AutoFlush总是让开发人员望而却步,所以他们经常只是禁用它。作为一个想要完全控制的开发人员,您不希望依赖某些似乎拥有自己生命的智能功能。 (尽管现实情况是你可能只是没有花时间完全搞定它)。

我可以想象,由于这个原因,EF团队决定不实施这样的功能。

那你该怎么做?不容易。添加新的context.StudentAttendance.Load(); 之后你会做...

DbContext

(假设你有一个context.Attach,虽然你的“var allAttendanceCount = context.StudentAttendance .Local // <= Local! Includes the new item .Where(att=> att.DayId == dayId) .Count(); ”似乎不符合这一点。

然后......

StudentAttendance

...你会得到理想的结果。但是,当然,在本地加载所有StudentAttendance个记录是一个巨大的过度杀伤。

一个简单的替代方法就是像现在一样对计数进行计数并添加1。

另一种方法是在进行计数之前模仿autoflush,排序并保存新的TransactionScope。但是你必须将所有东西都包裹在allAttendanceCount中。一个优点是您拥有{{1}}的最新值。