提示29. 怎样避免延迟加载或Load()阅读器问题

提示29. 怎样避免延迟加载或Load()阅读器问题

如果你有如下这样的代码:

 1 var results = from c in ctx.Customers
 2                    where c.SalesPerson.EmailAddress == “…”
 3                    select c;
 4 foreach(var customer in results)
 5 {
 6     Console.WriteLine(customer.Name);
 7     if (IsInteresting(customer))
 8     {
 9         customer.Orders.Load();
10         foreach(var order in customer.Orders)
11         {
12              Console.WriteLine(“\t” + order.Value);
13         }
14     }
15 }

这段代码将会打开2个同步的阅读器。一个枚举Customers,另一个枚举当前Customer的Orders。并且仅当 Multiple Active ResultSets(又称MARS)启用时才可用。所以如果MARS未启用你讲得到一个令人不快的异常。

注意:你可能会疑问为什么我要在这里调用 IsInteresting(..) 。因为如果没有这个判断,这种模式的代码是明确不推荐的。如果可以避免你不应该这样在循环中访问数据库,换句话说,如果你预先知道需要所有Customer的Order,你应该使用Include()来预先加载订单。

启用MARS很容易,只需将连接字符串中Multiple Active ResultSets=true;即可。

一般情况下不需要自己做,因为大多数连接字符串都是EF设计器创建的,其会为你进行这个设置。在3.5数据库优先及4.0模型优先的情况下都是如此。

但是如果"你"提供了ConnectionString,如Code Only中那样,你需要记得开启MARS。

因为这对3.5和4.0都有效,所以如果出错这两者机会也是均等的。

但是在4.0错误可能更隐蔽,因为新的LazyLoading特性(之前称作DeferredLoading)。

总之,问题的主旨是记得打开MARS!

提示30. 怎样使用自定义数据库函数(UDF)

想象你有一个像Nerd Dinner中DistanceBetween函数这样的数据库函数:

 1 CREATE FUNCTION [dbo].[DistanceBetween](
 2    @Lat1 as real,
 3    @Long1 as real,
 4    @Lat2 as real,
 5    @Long2 as real)
 6 RETURNS real
 7 AS
 8 BEGIN
 9 …
10 END

你想在Entity Framework使用它

声明这个函数

第一步在XML编辑器中打开EDMX文件,在<edmx:StorageModels>元素中<Schema>下添加一个<Function>元素。

完成后应该如下:

 1 <Function Name="DistanceBetween"
 2           IsComposable="true"
 3           Schema="dbo"
 4           Aggregate="false"
 5           BuiltIn="false"
 6           ReturnType="float">
 7    <Parameter Name="Lat1" Type="float" Mode="In"/>
 8    <Parameter Name="Long1" Type="float" Mode="In"/>
 9    <Parameter Name="Lat2" Type="float" Mode="In"/>
10    <Parameter Name="Long2" Type="float" Mode="In"/>
11 </Function>

在eSQL中使用函数

现在可以在eSQL中调用这个函数:

1 SELECT VALUE(D) FROM MyModel.Dinners AS D
2 WHERE StorageNamespace.DistanceBetween(
3       D.Latitude,D.Longitude,-34,174) < 50

MyModel就是你EntityContainer的名称(通常与ObjectContext相同),StorageNamespace是你的存储模型模式的命名空间。

在LINQ中使用函数

大部分人不使用eSQL,所以你可能会疑问怎样在LINQ中使用?

在3.5SP1中如下这样:

1 var nearbyDinners =
2      from d in ctx.Dinners.Where(
3 “StorageNamespace.DistanceBetween(it.Latitude, it.Longitude, –34, 174) < 50”
4 ) select d;

这里我们通过一个查询构造方法混合使用LINQ与eSQL,这个方法接收一个eSQL片段,在eSQL中调用了数据库函数。注意代码段通过‘it‘关键字关联到当前项。如果需要甚至可以关联到参数。

这很赞。

但是如果没有字符串会更好。

EF 4.0中的改进

在EF 4.0中你可以编写下面这样的代码来代替:

1 var nearbyDinners =
2     from d in ctx.Dinners
3     where DistanceBetween(d.Latitude, d.Longitude, –34,174) < 50
4     select d;

这看起来更好。没有上面那样的字符串并且支持编译时检查。

你需要一个这样的方法来使上面的代码工作:

1 [EdmFunction("StorageNamespace", "DistanceBetween")]
2 public double DistanceBetween(
3        double lat1,
4        double long1,
5        double lat2,
6        double long2)
7 {
8    throw new NotImplementedException("You can only call this method as part of a LINQ expression");
9 }

你可能会疑问为什么这个方法抛出一个异常?

我们从不真正需要直接执行这个方法。我们仅仅用它来编写LINQ查询,此查询会被翻译为SQL而不是实际调用这个方法。

EF使用EdmFuncation特性来得知哪个数据库函数需要代替这个函数被调用。

很酷吧。

好好享受。

提示31. 怎样组合L2O(LINQ to Objects)与L2E(LINQ to Entities)查询

考虑你想写一个如下这样的查询:

1 var possibleBuyers=
2      from p in ctx.People
3      where p.Address.City == “Sammamish” && InMarketForAHouse(p)
4      select p;

理论上只要InMarketForAHouse可以翻译为SQL这段代码就可以执行。

在EF4.0中可以通过为需要的Model或数据库函数创建一个CLR stub来实现。

假设如果没有对应的SQL。

可能这个功能需要使用所有那些不属于数据库的东西。

现在你不得不对查询进行“分割”。例如,将查询分割为基础的LINQ to Entities查询和依赖于L2E的LINQ to Objects查询。

你可能会尝试下如下这样的代码:

1 var partialFilter = from p in ctx.People
2                     where p.Address.City == “Sammamish”
3                     select p;
4 var possibleBuyers = from p in partiallyFilter
5                      where InMarketForAHouse(p);
6                      select p;

但是这几乎对代码的行为没有任何作用。 IQueryable(ctx.People) 仍然会被要求将 InMarketForAHouse(..) 翻译为SQL。

你需要调用 AsEnumerable() 方法,其可以有效地将查询独立为两部分:

1 var possibleBuyers = from p in partiallyFilter.AsEnumerable()
2                      where InMarketForAHouse(p);
3                      select p;

AsEnumerable() 确保LINQ to Objects处理所有的随后的请求。所以LINQ to Entities提供程序(如,ctx.People)用户不会知道 InMarketForAHouse() 方法。

当然有一些警告。

虽然最终的查询可能仅迭代一小部分记录,而实际发送到数据库的查询可能返回大量数据。

所以你需要考虑这将会发生什么。

问自己这样的问题:我会由数据库得到多少数据?

你甚至可能认为“迭代”大量数据没有问题。

问题是默认下你不仅仅在迭代记录。ObjectContext也为每个Entity进行标识识别,包括那些在后续LINQ to Objects中被丢弃的实体,这相当耗资源。

这个特定的问题可以“简单”的使用一个NoTracking查询来避开。

但是这又导致另一系列问题,你不能更新结果集,除非附加它们。

总之希望下一次你需要“分割”查询时你可以更多知道怎样权衡利弊。

提示32. 怎样由SSDL创建一个数据库 – 仅EF4.0

最近我们发布了一个扩展EF4 Beta 1的包含Code Only特性的CTP版本。

你可以在这里这里这里了解更多关于Code Only的信息。

如果你查看Code Only的代码走查,你将看到类似下面的代码:

1 // Create a builder and configure it
2 var builder = new ContextBuilder<MyContext>();
3 …
4 // Create a context
5 var mycontext = builder.Create(sqlConnection);
6 // Prepare the Context
7 if (!myContext.DatabaseExists())
8    myContext.CreateDatabase();

CreateDatabase() ,  DropDatabase() ,  DatabaseExists() 与 CreateDatabaseScripts() 均为发布于Code Only程序集的扩展方法。

这就是很优雅的事情:这些扩展方法与Code Only的剩余部分正相交。

你可以在任何ObjectContext中使用这些扩展方法,而不管其是否由Code-Only创建。

所以你可以在*任何*ObjectContext上调用这些方法。

想象这个场景:你团队中其他人签入一个EDMX作为项目的一部分,但是当你签出后你发现没有数据库脚本。现在你使用Code-Only来创建一个本地数据库。

这些着眼于数据库模型,例如生成并执行DDL,的方法在ObjectContext.MetadataWorkspace中有描述。

伴随这些提示总是有一些警告*:

1. 当前这仅工作于EF4 Beta1。当它们终止的时候我们将发布Code Only的新版本以与EF4较新的版本一同工作。

2. CreateDatabase()不知道怎样处理存储模型中的所有东西。例如如果你的EDMX引用到数据库视图或存储过程,Code Only将不知道怎样生成等效的数据库对象。

3. 当前这仅可与SQL Server一起工作。我们有计划给Code-Only添加提供程序模型,但是那还未实现。

尽管存在这些限制,毫无疑问CreateDatabase()与它同伴们会很有用。

编码愉快!

*没有警告这就不是一个提示了

提示33. 在EF中级联删除真正如何工作

考虑在数据库中你基于一个外键关系实现级联删除。

如下:

这个删除规则表名当一个Category被删除时所有相关的Product也被删除。

如果你由数据库生成一个EF模型,你得到的模型与表面看起来与一般的没有什么不同:

但如果你深入XML的CSDL部分,你会看到:

 1 <Association Name="FK_Products_Categories">
 2        <End Role="Categories" Type="TipsModel.Store.Categories" Multiplicity="1">
 3               <OnDelete Action="Cascade" />
 4        </End>
 5        <End Role="Products" Type="TipsModel.Store.Products" Multiplicity="*" />
 6        <ReferentialConstraint>
 7               <Principal Role="Categories">
 8                     <PropertyRef Name="ID" />
 9               </Principal>
10               <Dependent Role="Products">
11                     <PropertyRef Name="CategoryID" />
12               </Dependent>
13        </ReferentialConstraint>
14 </Association>

注意 <OnDelete> 元素,其通知EF,当一个Category被删除时,也*将*删除相关的Product。

我特意使用*将*而不是*应该*,因为EF不对数据库中的级联删除负责。

EF负责在调用 SaveChanges() 后维持ObjectContext的正确。所以EF常识同步ObjectContext到数据库完成预期的级联删除后预期的状态。

关于这个问题的存在一个说法,如果你打开如SqlProfiler之类的工具,你将注意到当一个主要元素被删除时,EF会在它知道的(如,那些被载入ObjectContext的)依赖主元素的实体上触发DELETE请求。

本质上会发生的是Entity Framework认为在删除数据库中主元素时将删除数据库中所有依赖主元素的东西。所以这就产生问题,什么呢,一个多余的DELETE来请求自己,导致已经加载相关对象被由ObjectContext中删除。

关键要注意的是EF*不会*真正检索数据库中的所有依赖实体并执行删除:它仅删除已经存在内存中的有依赖关系的对象。

所以如下是黄金法则:

  1. 如果你在模型中添加一个级联删除规则,你必须在数据库中有一个相应的DELETE规则。
  2. 如果由于一些原因你坚持打破规则(1),级联删除仅当你将所有依赖对象加载到内存中时才起作用。
  3. (2)是*不*被推荐的!!!

虽然我们尽全力使ObjectContext与数据库保持同步,但如果你有多层级联删除这种努力也会失败。

例如,如果你有如下这样的关系:

Category –> Product –> Order

删除一个Category的同时删除其中所有Product进而删除其Order。

EF可能,在极少的情况下,当你删除一个Category时无法与数据库同步。

例如,你有一个通过未加载的Product关联到一个Category的加载的Order,当你删除Category时,EF不知道应该删除Order。

这意味着Order会以unchanged状态留在ObjectContext中,尽管在数据库中其已被删除。

凡事预则立。

提示34. 怎样在EF中使用可更新视图

更新:谢谢Zeeshan指出默认情况下视图返回的实体中非空列最终会作为主键。

想象这种情况,你的数据库中有一个可更新的视图。

下一步你决定在Entity Framework中使用这个视图,所以你进一步导入这个视图。

产生的实体看起来像这样:

正如你所见,每一个属性都有个“钥匙”图标。

因为这个实体基于一个视图,EF不知道那些列组成主键,所以其假定每一个非空列都是主键的一部分。

固定主键

第一步要更改主键。在这个例子中ID是真正的主键。

可以在XML编辑器中打开EDMX,更改EntityType使其由如下这样,即每个属性都关联到<Key>:

变为这样:

一个很重要的需要注意的是,你不得不同时在EDMX的 <edmx:StorageModels> 与 <edmx:ConceptualModes> 节进行这个改动,因为两个模型需要在主键定义上达成一致。

将视图作为表对待

此刻你可以使用Entity Framework来查询Employees。

但是Entity Framework不允许你进行更新。

对于这个问题的一般方法是创建一个存储过程并以函数方式来使用它们。

但是考虑到视图已经具备更新的能力,以上方案显然不是很理想。

幸运的是有一个替代方案:简单的是EF认为此视图就是一个表。

这需要你更改EntitySet中StorageModel的定义。一般情况下开始时看起来像这样:

 1 <EntitySet Name="Employees"
 2            EntityType="Tip34Model.Store.Employees"
 3            store:Type="Views"
 4            store:Schema="dbo"
 5            store:Name="Employees">
 6   <DefiningQuery>SELECT
 7   [Employees].[ID] AS [ID],
 8   [Employees].[Firstname] AS [Firstname],
 9   [Employees].[Surname] AS [Surname],
10   [Employees].[Email] AS [Email]
11   FROM [dbo].[Employees] AS [Employees]
12   </DefiningQuery>
13 </EntitySet>

为了让其可以被作为表对待,替换为如下这样:

1 <EntitySet Name="Employees"
2            EntityType="Tip34Model.Store.Employees"
3            store:Type="Tables"
4            Schema="dbo" />

现在你可以执行任何CRUD操作。

很容易吧。

时间: 2024-10-18 03:53:46

提示29. 怎样避免延迟加载或Load()阅读器问题的相关文章

RSS阅读器FeedDemon使用方法

在搜索引擎中输入FeedDemon,点击第一个结果进入官网可直接下载,FeedDemon已宣布免费,安装后可在工具选项中选择语言简体中文. 在网络上找到RSS资源复制到订阅栏中,新建订阅. 更新后即可在阅读器中查看订阅网站的新闻. 可以在软件中设置更新间隔时间,网站信息更新时会自动收录信息并提示. 若是之前用过别的阅读器也可以导出原来的订阅列表,通过导入功能直接导入频道列表. 在右上角处可选择按标题  全文  摘要等显示格式. 可以在频道列表下的监视器中新建监视规则,有特定信息出现时自动通知.

自定义日志阅读器——包括了一个load取Tomcat日志的分析器

最近在写往公司产品里添加Tomcat适配器,以支持Tomcat.有一些功能需要摘取到Tomcat的部分日志.没有合适的工具,也不想去网上找了,就自己写了一个. 简单的画了一下设计方案: 下面直接上代码了: 日志阅读器: 1 package com.fjn.tools.log.reader; 2 3 import java.io.File; 4 import java.util.LinkedList; 5 import java.util.List; 6 import java.util.Scan

29 GroupSock(NetAddressList)——live555源码阅读(四)网络

29 GroupSock(NetAddressList)——live555源码阅读(四)网络 29 GroupSock(NetAddressList)——live555源码阅读(四)网络 简介 NetAddressList的定义 assign方法 NetAddressList的构造 clean方法与析构 拷贝构造与赋值运算符重载 NetAddressList::Iterator迭代器 本文由乌合之众 lym瞎编,欢迎转载 blog.cnblogs.net/oloroso 本文由乌合之众 lym瞎

作业部落 Cmd Markdown 编辑阅读器

Cmd Markdown 编辑阅读器 Cmd Markdown 编辑阅读器 WindowsMacLinux 全平台客户端 什么是 Markdown 书写一个质能守恒公式1 高亮一段代码2 高效绘制 流程图 高效绘制 序列图 绘制表格 更详细语法说明 什么是 Cmd Markdown 实时同步预览 编辑工具栏 编辑模式 实时的云端文稿 离线模式 管理工具栏 阅读工具栏 阅读模式 标签分类和搜索 文稿发布和分享 我们理解您需要更便捷更高效的工具记录思想,整理笔记.知识,并将其中承载的价值传播给他人,

小说阅读器

小说阅读器 启动模块bin/start.py 1 import os 2 import sys 3 4 BASE_PATH=os.path.dirname(os.path.dirname(__file__)) 5 sys.path.append(BASE_PATH) 6 7 8 from core import src 9 10 if __name__== '__main__': 11 src.run() 核心代码模块core/src.py 1 from db import db_handler

Android简单的编写一个txt阅读器(没有处理字符编码),适用于新手学习

本程序只是使用了一些基本的知识点编写了一个比较简单粗陋的txt文本阅读器,效率不高,只适合新手练习.所以大神勿喷. 其实想到编写这种程序源自本人之前喜欢看小说,而很多小说更新太慢,所以本人就只能找一个完本的.txt小说下载下来,有没有网络都可以看,当然现在不看那玩意了. 废话就不说了,程序中使用到的有4个类,5个xml文件,其中3个布局文件.String.xml.AndroidManifest.xml. 先看效果图吧(虽然很丑):     下面就上代码吧,本人都注释好了,由于本人技术还不行,注释

《Yii2 By Example》第2章:创建一个简单的新闻阅读器

第2章 创建一个简单的新闻阅读器 本章内容包含:创建第一个控制器,用于展示新闻条目列表和详情:学习控制器和视图之间的交互:自定义视图的布局. 本章结构如下: 创建控制器和动作 创建用于展示新闻列表的视图 控制器是如何将数据传送到视图的 例子--创建一个控制器,展示静态新闻条目列表和详情 将常用视图内容分割成多个可复用视图 例子--在视图中进行部分渲染 创建静态页面 在视图和布局之前共享数据 例子--根据URL参数更换布局背景 使用动态模块布局 例子--添加展示广告信息的动态盒 使用多个布局 例子

基于JSP的RSS阅读器

阅读器访问地址:http://easyrss.tk/,欢迎体验! 目录 一.    概述  二.    设计的基本概念和原理  1.    RSS与RSS阅读器概念 2.    阅读器实现原理 三.    设计方案 1.    架构设计 2.    数据库设计 3.    界面设计 4.    功能设计 5.    网络安全补充 四.    主要源代码  1.    视图部分 五.    阅读器使用说明 1.    注册与登录 2.    添加与管理RSS源 3.    阅读文章 概述 获得信息

C#与数据库访问技术总结(十二)数据阅读器(DataReader)2

遍历数据阅读器中的记录 当ExecuteReader方法返回DataReader对象时,当前光标的位置在第一条记录的前面. 必须调用阅读器的Read方法把光标移动到第一条记录,然后,第一条记录将变成当前记录. 如果数据阅读器所包含的记录不止一条,Read方法就返回一个Boolean值true. 想要移到下一条记录,需要再次调用Read方法.重复上述过程,直到最后一条记录,那时Read方法将返回false. 经常使用while循环来遍历记录: while(reader.Read()) { //读取