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新手,如果你发现一个菜鸟级错误请告诉我。

时间: 2024-10-04 03:11:24

Entity Framework技巧系列之十二 - Tip 46 - 50的相关文章

(翻译)Entity Framework技巧系列之十 - Tip 37 - 41

提示37. 怎样进行按条件包含(Conditional Include) 问题 几天前有人在StackOverflow上询问怎样进行按条件包含. 他们打算查询一些实体(比方说Movies),并且希望预先加载一个相关项目(比方说,Reviews),但又仅要那些匹配一些条件的reviews(如,Review.Stars==5). 不幸的是EF的预先加载对此没有完整的支持,如,对于 ObjectQuery<Movie>.Include(…) 方法,Include或者是全部加载或者是不加载任何东西.

Entity Framework技巧系列之十 - Tip 37 - 41

提示37. 怎样进行按条件包含(Conditional Include) 问题 几天前有人在StackOverflow上询问怎样进行按条件包含. 他们打算查询一些实体(比方说Movies),并且希望预先加载一个相关项目(比方说,Reviews),但又仅要那些匹配一些条件的reviews(如,Review.Stars==5). 不幸的是EF的预先加载对此没有完整的支持,如,对于 ObjectQuery<Movie>.Include(-) 方法,Include或者是全部加载或者是不加载任何东西.

Entity Framework技巧系列之五 - Tip 16 – 19

提示16. 当前如何模拟.NET 4.0的ObjectSet<T> 背景: 当前要成为一名EF的高级用户,你确实需要熟悉EntitySet.例如,你需要理解EntitySet以便使用 AttachTo(-) 或创建EntityKey. 在大部分情况下,针对每个对象/clr类型只有一个可能的EntitySet.Tip 13正是利用这种想法来简化附加(Attach)对象并且你也可以对Add使用类似的技巧. 然而为了在.NET 4.0中解决这个问题,我们添加了一个叫做 ObjectSet<T&

Entity Framework技巧系列之六 - Tip 20 – 25

提示20. 怎样处理固定长度的主键 这是正在进行中的Entity Framework提示系列的第20篇. 固定长度字段填充: 如果你的数据库中有一个固定长度的列,例如像NCHAR(10)类型的列,当你进行一次插入时,填充会自动发生.所以例如如果你插入'12345',你将得到5个自动填充的空格,来创建一个10个字符长度的字符串. 大多数情况下,这种自动填充不会有问题.但是在使用Entity Framework时如果你使用这些列的一个作为你的主键,你可能会在进行标识识别(identity resol

Entity Framework技巧系列之二 - Tip 6 - 8

提示6. 如何及何时使用贪婪加载 什么时候你需要使用贪婪加载? 通常在你的程序中你知道对查询到的实体将要进行怎样的操作. 例如,如果你查询一个订单以便为一个客户重新打印,你知道没有组成订单的项目即产品的信息重打印将是不完整的,所以你知道你将需要同时加载这些信息. 这是贪婪加载起作用的一类场景. 如果你知道你需要额外信息,或实体,你可能也会预先加载这些实体(贪婪加载),因为这将省下生在将来的查询. 怎样进行贪婪加载? 与一些普遍存在的错误观念相反,Entity Framework中贪婪加载即可行也

Entity Framework技巧系列之十三 - Tip 51 - 55

提示51. 怎样由任意形式的流中加载EF元数据 在提示45中我展示了怎样在运行时生成一个连接字符串,这相当漂亮. 其问题在于它依赖于元数据文件(.csdl .ssdl .msl)存在于本地磁盘上. 但是如果这些文件存在于web服务器中或者类似的位置,甚至你无权访本地文件系统而无法把它们拷贝到本地呢? 原来你也可以由流中加载元数据,这篇提示将告诉你怎么做. 步骤1:获得用于CSDL,MSL与SSDL的XmlTextReaders: 这可以尽可能的简单,如'new XmlTextReader(url

Entity Framework技巧系列之七 - Tip 26 – 28

提示26. 怎样避免使用不完整(Stub)实体进行数据库查询 什么是不完整(Stub)实体? 不完整实体是一个部分填充实体,用于替代真实的对象. 例如: 1 Category c = new Category {ID = 5}; 就是一个不完整实体. 这个实体中只有ID被填充,表示这是一个代表Category 5的Stub. Stub实体什么时候有用? 当你真正不需要知道一个实体的一切对象时,Stub实体就很有用,主要因为通过使用这种实体你可以避免不必要的查询,但也因为它们比EntityKey更

Entity Framework技巧系列之八 - Tip 29 – 34

提示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

Entity Framework技巧系列之十一 - Tip 42 - 45

提示42. 怎样使用Code-Only创建一个动态模型 背景: 当我们给出使用Code-Only的例子,总是由创建一个继承自ObjectContext的强类型的Context开始.这个类用于引导模型. 例如这个类(处于简化问题考虑省略了属性体): 1 public class MyContext : ObjectContext 2 { 3 public ObjectSet<Category> Categories { get; } 4 public ObjectSet<Product&g