Entity Framework技巧系列之十二 - Tip 46 - 50
提示46. 怎样使用Code-Only排除一个属性
这次是一个真正简单的问题,由StackOverflow上这个问题引出。
问题:
当我们使用Code-Only把一个类的信息告诉Entity Framework,默认情况下每个属性会成为Entity的一部分,并作为一个存储于数据库中的结果。
通常这是你想要的结果。
但是也有例外,考虑这个类:
- 1 public class Person{
- 2 public int ID {get;set;}
- 3 public string Firstname {get;set;}
- 4 public string Surname {get;set;}
- 5 public string Fullname {get;set;}
- 6 }
这里Fullname实际上仅是Firstname与Surname联接在一起,所以将Fullname单独存储于数据库中会有冗余。你会想要忽略Fullname。
该怎样做呢?
解决方案1 – 移除Setter – 亦'一般不做为选择'
要成为'Entity'的一个属性,Entity Framework必须可以读写这个属性,所以如果你移除setter,这个CLR的中属性将不再成为'Entity'的属性。
不幸的是不总是有可能做这类事,更重要的是做这种*只*可以工作于Code-Only中的东西与Persistence Ignorance相反。
我们需要另一种解决方案…
解决方案2 – 显示映射你想要的所有属性
如果你进行显示映射,你由映射中排除的属性会被忽略,而不会允许Code-Only按照惯例来映射。
所以以下这个映射:
- 1 builder.Entity<Person>().MapSingleType(p => new {
- 2 p.ID,
- 3 p.Firstname,
- 4 p.Surname
- 5 });
伴随着指定映射带来的副作用是需要告诉Code-Only忽略FullName属性。
长期解决方案?
显然需要强制映射每一个属性而*只*是为了排除一个属性不是一种理想的解决方案,尤其当其它一切属性都是"按惯例的"。
像这样的东西会更好:
- 1 builder.Entity<Person>().Exclude(p => p.Fullname);
当前用于Beta2的CTP2版中还不支持这种写法,但这会被考虑加入将来版本的Code-Only中…
提示47. 为什么fix-up会使更改关联变得困难
问题:
看这段代码:
- 1 Category oldCategory = ctx.Categories
- 2 .Include("Products")
- 3 .First(c => c.Name == "Drink");
- 4
- 5 Category newCategory = new Category {Name = "Beverage"};
- 6
- 7 foreach(Product product in oldCategory.Products)
- 8 {
- 9 newCategory.Products.Add(product);
- 10 }
在这个例子中整个解决方案可能仅是为了将oldCategory重命名为”Beverages” – 如果是实际中的例子这会变得越来越困难,你在微软泡沫中工作的时间会更长:)。
关键是你试图将Product由一个Category移动到另一个,或者更抽象的说我们正将实体由一个集合关系移动到另一个。
不幸的是上面的代码会失败:
下层的集合被改变了?
什么?
我们没有改变它!
对吗?!
错。
相反我们触发了一个更改。
如果一个Product只可以属于一个Category,给Product指定一个不同的Category会导致EF将其由旧的Category(即oldCategory.Products)中自动移除。
所以添加到一个新Category并由旧Category移除会导致迭代无法继续,因为‘集合被修改’。
注意:错误发生于你试图由oldCategory.Products集合中获取第二个产品时。
解决方案:
一旦你理解了这个问题,解决方案就不值一提了。
在你更改任何关系前迭代旧的集合,使用ToArray()或一个类似的方法。
- 1 var products = oldCategory.Products.ToArray();
- 2 foreach(Product product in products)
- 3 {
- 4 newCategory.Products.Add(product);
- 5 }
正如你看到的,变通方案并不是完全是难事。
另一方面,了解你为什么需要变通方案更重要;)
提示48. 怎样WCF中宿主一个数据服务
许多人想知道是否可以在WCF中宿主ADO.NET Data Service。
答案是肯定的,事实上只要你设置好引用就可以,很简单。
步骤1 – 建立你的项目
我做的项目如下这样:
这个例子中我使用的是VS2010 beta2,在VS 2008 SP1中这同样有效。
如你所见项目中有一个控制台应用程序,一个名为ProductsContext的EF实体数据模型与一个名为ProductsCatalog.svc数据服务来暴露ProductsContext。
实际上将ProductsCatalog.svc加入控制台应用需要一点技巧-如果你尝试使用添加新项是找不到添加数据服务这项的,因为这个对话框是项目类型敏感的,所以它会过滤掉它认为不像一个控制台应用的选项-我通过创建一个数据服务类型的临时Web应用并将其拷贝到我的控制台应用项目来实现这个目的。
你需要添加到适当程序集System.Data.Entity(EF), System.Data.Services&System.Data.Services.Client(Astoria)与System.ServiceModel&System.ServiceModel.Web(WCF)。
现在我们把项目准备好了,下一步…
步骤2 – 暴露你的数据服务
现在,Phani提供了这些代码,这就是暴露数据服务所需的全部:
- 1 string uriBaseAddress = "http://localhost:998";
- 2 Uri[] uriArray = { new Uri(uriBaseAddress) };
- 3 Type serviceType = typeof(ProductsCatalog);
- 4 using(WebServiceHost host = new WebServiceHost(serviceType, uriArray))
- 5 {
- 6 try
- 7 {
- 8 host.Open();
- 9 Console.ReadKey();
- 10 }
- 11 catch (Exception ex)
- 12 {
- 13 Console.WriteLine("An exception occurred:");
- 14 Console.WriteLine(ex.ToString());
- 15 host.Abort();
- 16 }
- 17 Console.WriteLine("Aborting");
- 18 Console.ReadKey();
- 19 }
如你所见我们简单的为数据服务类创建了一个WebServiceHost,并提供你希望服务绑定到的URL。
现在你应该可以完成如图所示的操作了:
所有一切都是通过一个小控制台应用程序来暴露的。
有意思吧?
提示49. 怎样找到数据服务的bug
最近一段时间我一直在尝试创建一个自定义的数据服务提供程序,又称DSP(注:Data Service Provider的缩写)。
到目前为止我已设法设置好所有的元数据,所以浏览$metadata可以很好的工作。我也完成了一些简单的查询工作。
基本上一切很好。
至少直到我尝试这个URL:
我不能准确的知道发生了什么,并且我也不能由Fiddler得到任何帮助,因为出现问题的服务运行在内置的VS开发服务器上。
并且因为我的数据服务提供程序估计设计为松散类型,我需要辨别出这个错误的:
与这个正确的:
之间的不同。
你能看出问题吗?
如果能这很好,你的眼力比我强…
结果你不需要好眼力,你只需了解DataService<>的HandleException,你可以像这样重写这个方法:
一旦我进行了这步,可以很容易辨别出我的一个字典对象中有一个以ID而不是以Id为key的项。
修复这个问题后,查询就可以很好的工作了:
当开发一个数据服务提供程序时了解HandleException绝对至关重要,但它也有更一般的用途,例如审核,错误记录等你能叫得上名字的功能。
噢,你注意到我的数据服务提供程序暴露了来自字典对象列表的数据了吗?
那是另一天的故事J
提示50. 怎样使用JQuery查询数据服务
最近我花了一些时间尝试JQuery。
因为数据服务可以以JSON格式暴露数据,我认为可以使用JQuery由数据服务获取一些数据。
结果这相当容易。
这个例子不会赢得任何奖项,但是它会展示给你基础并帮助你早日上手。
HTML:
首先我制作了一个页面:
- 1 <html xmlns="http://www.w3.org/1999/xhtml">
- 2 <head>
- 3 <title></title>
- 4 </head>
- 5 <script src="Scripts/jquery-1.3.2.js" type="text/javascript">
- 6 </script>
- 7 <script type="text/javascript">
- 8 // JSON CODE IS GOING TO GO HERE
- 9 </script>
- 10 <body>
- 11 <a href='#' id="aShowProducts">Show Products</a><br />
- 12 <a href='#' id="aShowCategories">Show Categories</a><br />
- 13 <div id="divResults" />
- 14 </body>
- 15 </html>
如你所见这很简单。
JQuery Code
下一步是JQuery代码。
使用JQuery的第一步总是编写订阅Document加载完成的事件的代码:
- 1 var divResults;
- 2 var aShowProducts;
- 3 var aShowCategories;
- 4 var ajaxRequest;
- 5
- 6 $(document).ready(function () {
- 7 divResults = $('#divResults');
- 8 aShowCategories = $('#aShowCategories');
- 9 aShowProducts = $('#aShowProducts');
- 10 aShowProducts.click(function () {
- 11 GetData('Products');
- 12 });
- 13 aShowCategories.click(function () {
- 14 GetData('Categories');
- 15 });
- 16 });
当文档加载完成,首先我设置一些全局变量:
l divResults (如 <div id="divResults" /> ): 承载查询的结果。
l aShowProducts (如 <a href="#" id="aShowProducts" …</a> ): 'Show Products'链接
l aShowCategory: 'Show Categories'链接
l ajaxRequest: 是一个承载当前请求的变量,所以如果需要我们可以终止它。
接下来我给两个超链接的点击事件订阅GetData方法,并给方法传入适当的Data Service资源集名称。
获取结果
GetData函数如下所示:
- 1 function GetData(set) {
- 2 var materializer;
- 3 switch (set) {
- 4 case 'Categories':
- 5 materializer = GetRowForCategory;
- 6 break;
- 7 case 'Products':
- 8 materializer = GetRowForProduct;
- 9 break;
- 10 default:
- 11 alert('problems');
- 12 }
- 13
- 14 if (ajaxRequest!= null)
- 15 ajaxRequest.abort();
- 16
- 17 ajaxRequest= $.getJSON(
- 18 "ProductsService.svc/" + set,
- 19 function (data) {
- 20 var array = [];
- 21 array.push("<table>");
- 22 $.each(data.d, function (i, item) {
- 23 materializer(array, item)
- 24 });
- 25 array.push("</table>");
- 26 divResults.html(array.join(""));
- 27 }
- 28 );
- 29 }
这个操作取决于'set'参数,它选择一个函数用来取得查询结果,这个函数将为相应的资源类型生成一个<tr>。
所以对于'Categories'集我使用这个函数:
- 1 function GetRowForCategory(array, item)
- 2 {
- 3 array.push("<tr><td>");
- 4 array.push(item.Id);
- 5 array.push("</td><td>");
- 6 array.push(item.Name);
- 7 array.push("</td></tr>");
- 8 }
对于'Products'集函数如下:
- 1 function GetRowForProduct(array,item)
- 2 {
- 3 array.push("<tr><td>");
- 4 array.push(item.Id);
- 5 array.push("</td><td>");
- 6 array.push(item.Name);
- 7 array.push("</td></tr>");
- 8 }
一旦我们选择了正确的实体化器,下一步我们取消一切进行中的AJAX请求。
接着使用 $.getJSON(…) 初始化一个新的AJAX请求来获取由"ProductsService.svc/Products"或"ProductsService.svc/Categories"获得JSON。
结果的JSON(即数据)被传入回调函数用于构建一个html表格,通过:
l 使用 array.push(…) 这个方法来生成html字符串。
l 接着使用 $.each(data.d) 遍历实体集合,并调用特定的实体化器来为每一个实体创建一个新的 <tr />
自己尝试
完成后的页面在这里,你可以另存为查看。
你需要做的就是把这个html文件放到与你的DataServie相同的网站中,更改url来指向你的DataServie并修改实体化器选择器的代码与实体化器本身来匹配你的资源集与资源类型。
不是太难。
下一步需要有更多的经验以构建更多有趣的查询,见提示44获取些灵感。
BTW,我是Jquery新手,如果你发现一个菜鸟级错误请告诉我。
Entity Framework技巧系列之十二 - Tip 46 - 50的更多相关文章
- Entity Framework技巧系列之十四 - Tip 56
提示56. 使用反射提供程序编写一个OData Service 在TechEd我收到一大堆有关将数据作为OData暴露的问题. 到目前为止你大概知道可以使用数据服务与Entity Framework将 ...
- (翻译)Entity Framework技巧系列之十 - Tip 37 - 41
提示37. 怎样进行按条件包含(Conditional Include) 问题 几天前有人在StackOverflow上询问怎样进行按条件包含. 他们打算查询一些实体(比方说Movies),并且希望预 ...
- Entity Framework技巧系列之十 - Tip 37 - 41
提示37. 怎样进行按条件包含(Conditional Include) 问题 几天前有人在StackOverflow上询问怎样进行按条件包含. 他们打算查询一些实体(比方说Movies),并且希望预 ...
- Entity Framework技巧系列之五 - Tip 16 – 19
提示16. 当前如何模拟.NET 4.0的ObjectSet<T> 背景: 当前要成为一名EF的高级用户,你确实需要熟悉EntitySet.例如,你需要理解EntitySet以便使用 At ...
- Entity Framework技巧系列之六 - Tip 20 – 25
提示20. 怎样处理固定长度的主键 这是正在进行中的Entity Framework提示系列的第20篇. 固定长度字段填充: 如果你的数据库中有一个固定长度的列,例如像NCHAR(10)类型的列,当你 ...
- Entity Framework技巧系列之二 - Tip 6 - 8
提示6. 如何及何时使用贪婪加载 什么时候你需要使用贪婪加载? 通常在你的程序中你知道对查询到的实体将要进行怎样的操作. 例如,如果你查询一个订单以便为一个客户重新打印,你知道没有组成订单的项目即产品 ...
- Entity Framework技巧系列之十三 - Tip 51 - 55
提示51. 怎样由任意形式的流中加载EF元数据 在提示45中我展示了怎样在运行时生成一个连接字符串,这相当漂亮. 其问题在于它依赖于元数据文件(.csdl .ssdl .msl)存在于本地磁盘上. 但 ...
- Entity Framework技巧系列之十一 - Tip 42 - 45
提示42. 怎样使用Code-Only创建一个动态模型 背景: 当我们给出使用Code-Only的例子,总是由创建一个继承自ObjectContext的强类型的Context开始.这个类用于引导模型. ...
- Entity Framework技巧系列之八 - Tip 29 – 34
提示29. 怎样避免延迟加载或Load()阅读器问题 如果你有如下这样的代码: 1 var results = from c in ctx.Customers 2 where c.SalesPerso ...
随机推荐
- php生成图片缩略图的类方法
//php生成缩略图片的类 class ResizeImage{ public $type;//图片类型 public $width;//实际宽度 public $height;//实际高度 publ ...
- __stack_chk_fail栈检查失败
1. __stack_chk_fail的作用 在了函数的局部变量和保存的指令指针(译注:此处指返回地址和EBP)之间.这个值被称作金丝雀(“canary”)值 参考 http://www.freebu ...
- SQL语句的优化
1.创建索引 表中数据经常需要用的哪些字段数据进行过滤,则添加该字段的索引,索引相当如一本书的目录,能加快查询数据的速度:同时在新建索引的时候,将需要查询的列,在包含性 列中新增上去,能减少查询语句的 ...
- Maven搭建struts2+spring+hibernate环境
Maven搭建struts2+spring+hibernate环境(一) 本文简单的使用STS的自带的maven插件工具搭建ssh(struts2+spring+hibernate)开发环境,图文并茂 ...
- sublime text2教程
代码编辑器或者文本编辑器,对于程序员来说,就像剑与战士一样,谁都想拥有一把可以随心驾驭且锋利无比的宝剑,而每一位程序员,同样会去追求最适合自己的强大.灵活的编辑器,相信你和我一样,都不会例外. 我用过 ...
- xcb编译
sed -i "s/pthread-stubs//" configure && ./configure $XORG_CONFIG \ --enable-xinput ...
- MySQL Logs
摘要 一.MySQL日志 1.1 查询日志 1.2 慢查询日志 1.3 错误日志 1.4 二进制日志 一. MySQL日志 MySQL服务器上一共有六种日志:错误日志,查询日志,慢查询日志,二进制日志 ...
- ActiveMQ in Action(2) - Transport
关键字: activemq 2.2 Transport ActiveMQ目前支持的transport有:VM Transport.TCP Transport.SSL Transport.Peer ...
- PHP下用正则表达式分割preg_split、替换reg_replace、匹配preg_match_all等出现乱码的解决方法
操作前声明操作字符的编码: mb_regex_encoding('utf-8'); $arr = preg_split('/[\n,]/u',$data['name'] ,0, PREG_SPLIT_ ...
- PHP中使用CURL(五)
curl伪造IP和来源 client.php请求server.php client.php <?php $ch = curl_init(); curl_setopt($ch, CURLOPT_U ...