数据点

几个我最喜欢的东西...在实体框架 4.2 DbContext

朱莉列尔曼

Julie Lerman
甚至在 2011 年初发布的实体框架 4.1 之前,开发人员聚焦于只有一半的什么该包中给我们:第一次的代码。代码首先可以表达你实体数据模型使用您的域类和代码第一次配置,一个不错的选择,开发人员不想使用可视设计器中定义的模型。但每一位的示例代码,您可以看到使用这些类和代码第一次定义模型使用实体框架 (EF) 由 EF 4.1 进来的另一个非常重要的功能:DbContext 类中。

ObjectContext 类是核心 microsoft EF API 的一部分。NET 框架 4,允许您执行查询、 更改跟踪和更新数据库的类使用强类型的类,代表您的模型。DbContext 类是最贴切的描述,如公开最通常的 ObjectContext 的包装使用的 ObjectContext 功能,以及提供了一些简单的"快捷方式",是经常使用但复杂的代码直接与 ObjectContext 的任务。

这是我的指引,微软的你应该首先考虑 DbContext,开始使用 EF 的新项目时。如果你发现你有时需要访问的在更精细的逻辑,ObjectContext 类提供了一些,有钩从 DbContext 实例去其底层的 ObjectContext:

var objectContext = (myDbContextInstance as IObjectContextAdapter).ObjectContext

如果你知道你会做直接需要频繁使用的 ObjectContext 功能的工作,您可能更愿意使用,而不是 DbContext。 但一般情况下,EF 团队建议您避免直接使用 ObjectContext,除非禁止您使用 DbContext,由于某种原因。

我将添加告诫人们,本指南旨在为新项目。 当使用 DbContext API,你不仅的新轻薄和更聪明的 DbContext 类,而且也同样改进的 DbSet 和 DbQuery 类 (与对应的 ObjectSet 和 ObjectQuery)。

虽然我是个狂热的 DbContext,几个功能已成为我最喜欢的小宠物。

DbSet.Find

API 中的新方法之一是 DbSet.Find。 这有助于与用于数据访问的常见模式开发者使用:正在检索单个对象基于其主键。

ObjectContext,你将不得不创建一个完整的查询,然后执行查询使用 LINQ 方法,例如 SingleOrDefault。

这看起来类似:

var partyHatQuery = from p in context.PartyHats where p.Id == 3 select p;
var partyHatInstance = partyHatQuery.SingleOrDefault();

您可以编写,更有效地使用 LINQ 方法和 lambda:

var partyHatInstance = context.PartyHats.SingleOrDefault(p => p.Id == 3);

此简单任务执行的查询执行多久了? 你可能有甚至抽象此代码在您自己的更简单方法。

这是只是 EF 团队为您 DbContext API 中。 时使用的 DbContext,PartyHats 将是 DbSet <PartyHat>,您可以使用 DbSet.Find 方法很快达到相同的查询执行,与:

context.PartyHats.Find(3)

此方法假定您提供的值是您正在搜索的类的密钥值 — — 在本例中为 PartyHat。 EF 然后将代表您搜索 Id 等于中传递的值的数据执行 SingleOrDefault 查询 — — 在此情况下,3。 你很可能就会通过在变量中,不是实际的值。

有的你不能达到与查询的 DbSet.Find 方法的另一个好处。 Find 方法首先会看在正在跟踪的上下文的匹配对象的内存。 如果找到,EF 然后就不会打扰查询数据库。 这是比只是要扔掉的查询结果,如果对象实例已在内存中的数据库上执行的查询的高效得多 — — 浪费的访问数据库的许多开发人员触发而不自知。

您还可以使用 DbSet.Find 与复合键。 查找的签名不是采取单个对象而采取的参数数组。 因此,您可以传递的值来代表组成键的值的列表中。

DbSet.Local

当使用外汇基金,我经常发现自己想要做些什么已经在内存和正在跟踪的上下文的对象。 在 SaveChanges 重写或 SavingChanges 方法,我就会在其中执行一些验证此逻辑的典型地方。 (多亏了新的验证 API 可与 DbContext 一起,我已经能够减少很多这种逻辑。 但我不会在本专栏中讨论验证)。

ObjectContext 不会提供的方法来发现的对象的跟踪,但为此 API 逻辑是既不容易找到,也不是容易的代码。 实际上,在我的书,"编程实体框架"(O'Reilly 介质,2010年),我写了一组的四个方法帮助使此任务更简单、 更灵活的扩展。

更常见的是,不过,开发商没有意识到 LINQ to 实体查询执行上下文和与这些上下文已在跟踪的对象交互之间的区别。 例如,我见过很多开发人员检索数据使用查询,并随后试图要上什么目前管理查询所执行的逻辑代码:

var balloons = context.Balloons.Where(b => b.Size == "L").ToList();
 var balloonCount = context.Balloons.Count();

事实上,这些是两个单独的查询。 代码的第二行在数据库上执行另一个查询,并返回所有的气球的计数。 通常情况下,开发人员的用意是要获取计数结果的 — — 就是气球。计数。

如果您不能访问的变量,但仍然想要找出多少气球对象 ObjectContext 跟踪,有一种方法找出,但并不容易:ObjectContext 公开 ObjectState­管理器,它有一个称为 GetObjectStateEntries 的方法。 此方法要求您传递中一个或多个 EntityState 枚举 (例如,增加,修饰等) 所以它知道返回的项。 虽然结果可查询,过滤是笨拙,即使在当时它返回什么不是您的实体,而是代表有关您的对象状态信息的 ObjectStateEntry 实例。

这意味着无需使用我的扩展方法,以帮助的气球的计数在内存中的代码看起来是这样:

objectContext.ObjectStateManager
 .GetObjectStateEntries(EntityState.Added |
                        EntityState.Modified | EntityState.Unchanged)
 .Where(e => e.Entity is Balloon).Count();

如果您要捕获这些气球的对象,不只是 ObjectStateEntry 实例,您必须添加一些铸造返回作为气球 ObjectStateEntry.Entity 类型:

objectContext.ObjectStateManager
 .GetObjectStateEntries(EntityState.Added |
                        EntityState.Modified | EntityState.Unchanged)
 .Where(e => e.Entity is Balloon)
 .Select(e => e.Entity as Balloon);

看到此代码可能会让你领略新属性 DbSet.Local,几乎是我做。

获取所有履带气球实例从上下文中使用 DbSet.Local,您可以简单地调用:

context.Balloons.Local;

"本地"返回两个优点的 ObservableCollection。 首先是可查询,以便您可以将返回的本地缓存的气球的任何子集你想要的。 第二是您的代码 (或组件如数据绑定控件) 可以侦听并正在添加或从缓存中移除的对象作出反应。

除了可发现属性和减少了的代码,还有两个其他使用 DbSet.Local 和 GetObjectStateEntries 之间的显著差异。 一个是本地返回对象,从特定的 DbSet 只,而 GetObjectStateEntries 返回无论它们表示的对象的类型的项。 其他不同的是本地不会返回对象的上下文知道被标记为已删除。 使用 GetObjectStateEntries,您可以为您提供的方法的参数列表中指定的增加、 修改日期、 未更改、 已删除对象的访问。

NoTracking LINQ 查询

在讨论时与客户端的性能,经常推荐他们利用 EF 返回并不需要通过上下文进行跟踪的数据的能力。 例如,您可能需要提供下拉选择列表的数据。 您永远不需要对该数据进行更改,那么它持续到数据库。 因此,它是智能避免性能 EF 创建 ObjectStateEntry 实例为每个对象,它跟踪,以及强制要注意这些对象所做的任何更改的上下文时所采取的打击。

但与 ObjectContext,NoTracking 支持仅通过 ObjectQuery 类,不能从 LINQ to 实体查询可用。

下面是一个典型的例子,使用 (称为上下文) ObjectContext NoTracking 查询得到的:

string entitySQL = " SELECT p, p.Filling " +
                           "FROM PartyContext.Pinatas AS p " +
                           "WHERE p.Filling.Description='Candy'";
var query=context.CreateQuery<DbDataRecord>(entitySQL);
query.MergeOption = System.Data.Objects.MergeOption.NoTracking;
var pinatasWithFilling=query.ToList();

检索到的锅及补牙将对象在内存中,但背景下会有不了解它们。

然而,如果您要使用以下 LINQ 到实体的查询,返回 IQueryable,而不是 ObjectQuery,将没有合并属性:

context.Pinatas.Include("Filling")
  .Where(p=>p.Filling.Description=="Candy")

一个解决方案是投到 ObjectQuery 的 LINQ 查询,然后设置合并。 这是不仅不明显,而且也笨重。

认识到这一点,EF 团队找到让你有你方的蛋糕,吃它时,也是 DbContext API 的一部分的 IQueryables AsNoTracking 扩展名的新方法与方法。 现在我可以转向到我的 LINQ 查询:

context.Pinatas.Include("Filling")
  .Where(p=>p.Filling.Description=="Candy")
  .AsNoTracking();

这将返回一组 Pinatas 和将被忽略的上下文的填充物。 EF 不会 wastethe DbEntityEntry 对象 (ObjectStateEntry DbContext API 版本) 为每个对象实例化的努力。 也不将它浪费检查这些对象调用 DetectChanges 时强制上下文的努力。

它是简单的代码,并很容易发现通过智能感知。

蛋糕上的糖衣

这三个特点 — — 查找,本地和 AsNoTracking — — 别让我可以执行的任务不是用 ObjectContext 可以实现。 但它们让我快乐每次我使用它们。 有这么多编码 DbContext API 简化了 (与比较使用 ObjectContext) 的任务,它已简化我的应用程序开发不少。 我也回到了 ObjectContext 的旧代码,它使用与代码第一 DbContext 和已经能够显著减少这些应用程序中的代码的重构。 但对于开发人员不像我一样密切熟悉 EF,发现许多其功能的能力会有很大的差异,为启动并运行它。

朱莉列尔曼 是 Microsoft MVP。净的导师和顾问住在佛蒙特州的丘陵。您可以查找数据访问和其它微软对她提出。用户组和世界各地的会议的净主题。在她的博客 thedatafarm.com/blog 和"编程实体框架"的作者是 (2010 年) 和"编程实体框架:代码第一次"(2011 年),两者都从 O'Reilly 媒体。跟着她在 Twitter 上 twitter.com/julielerman

多亏了以下的技术专家审查这篇文章: 亚瑟维氏