Entity Framework技巧系列之一 - Tip 1 - 5

时间:2021-01-04 09:15:35

提示1. 在Entity Framework中怎样排序关系(Relationships)

问题:

Entity Framework论坛中常会看到关于排序相关联项目的问题。

例如,想象你要查询客户,并返回那些欠款超过30的账户,与此同时检索这些账户的订单。

并且你需要将那些订单按下单日期排序,这样你可以首先看到最近的订单,以便轻松的查找到可疑的行为。

答案:

大部分人们可能都知道EF中可以使用Include()即时加载一个关系。如下示例:

1 var lateCustomers = from c in ctx.Customers.Include("Orders")
2 where c.IsMoreThan30DaysInArrears
3 select c;

但不幸的是,这个语句返回的每个客户的订单都是无序的。

所以你怎样排序这些订单呢?严格地说没有办法。

但是你也可以做一些这样的工作,如在select子句中进行include操作。

Entity Framework技巧系列之一 - Tip 1 - 5
1 var lateCustomers = from c in ctx.Customers
2 where c.IsMoreThan30DaysInArrears
3 select new {
4 Customer = c,
5 Orders = c.Orders.OrderByDescending(
6 o => o.OrderDate
7 )
8 };
Entity Framework技巧系列之一 - Tip 1 - 5

这个语句使用标准的LINQ语义请求一个由客户及其排序过的所有订单组成的匿名类型的迭代。

Entity Framework可以很好的支持这种写法所以问题得到解决…

补充知识:

以上做法有几分…

我没有使用实体来获取数据,而是使用了其它东西 – 一个匿名类型。

这种方式不是很理想。

你很想通过customer.Orders属性来访问客户的订单。有趣的是即使名为Fix-up的东西已经实现了这个目标。

Entity Framework的对象服务会自动将相关联的事物绑定在一起,这个过程就称为Fix-up。

一般情况下,Fix-up这个过程发生在两个相关联的实体进入上下文的那一刻。

所以因为我已经加载了Customer与Orders两者(通过投影),Fix-up也将会保证customer.Orders属性包含那些订单。这是我的查询中没有使用Include("Orders")的真正原因。

这回避了问题,从而引出了新问题:Customer.Orders可以被排序吗?

不幸的是,目前没有官方解决方式。

这不是我们要支持的特性,我们没有花时间 – 必要的时间来测试它,确保其可以在每种场景下工作。你知道那都是重要的QA问题。

虽然有反对者,但无论如何这可以工作…

为什么呢?

我的猜测是在查询中执行排序,编写基于DataReader来实例化实体并最终进行Fix-up的代码是有副作用的。

现在,理论上你可以在项目中使用这个特性…但是,注意这是个大大的转折,请明白这是不推荐的。

依赖于一种有潜在的负面作用的特殊实现总是存在一点风险。

提示2. Entity Framework书籍

问题:

在哪可以找到深入学习Entity Framework的书籍?

答案:

在微软产品小组的产品上做开发的一个好处是可以接触到想让你审阅他们书籍的作者。

到目前为止我收到3位不同的作者发给我的他们的Entity Framework书籍的书稿。

如下是我按收到它们的顺序:

ADO.NET Entity Framework - Unai Zorrilla Castro / Octavio Hernandez / Eduardo Quintas

这本书是用西班牙语写的,本质上单一语言的,所以个人很难对其质量进行评论,但是我的阿根廷同事推荐这本书,对我这就足够好了。尽管如此,我还是喜欢将其放在我的书架上,这让我看起来很先进…所以谢谢Unai。

Programming Entity Framework - Julie Lerman

我与Julie进行过几次个人会面,我知道她在这本上倾注了她的心血。所以这本将近800页的书讲的如此透彻也就不令人吃惊了。Julie的书中包含了大量有用的信息以及一些现实中的例子。

Julie,谢谢你的书。

Professional ADO.NET 3.5 with LINQ and the Entity Framework - Roger Jennings

我从没见过Roger,但是从他的博客上可以明确的知道他使我们感觉可靠。他的书与Julie的书有一点不同,因为其讲述了Entitiy Framework周围其它一些事物,如LINQ to XML,LINQ to DataSet等。我昨天刚收到这本书(谢谢Roger),但是我相信其很有市场。

正如在这看到的这些很好选择…

祝你的Entity Framework顺利

提示3. 开始T4之旅

如果你读过Entity Framework Design Blog,你应该已经听我们讨论过T4。这是一项与Visual Studio 2008一起发行的技术(2005有一个独立的版本供下载)。

在.NET4.0中,Entity Framework使用了T4来增强代码生成与模型初始化这种场景。

事实上T4现在也用在其它一大些微软的产品,包括ASP.NET MVC和Dynamic Data。

所以考虑如果现在要开始使用T4并开始熟悉这种技术,你应该怎么做?

事实上这种技术相对简单。你可以非常轻松的完成相当有用的事情:

  • 向项目中添加一个文本文件,并将扩展名改为".tt"。
  • 在文本文件中写入一些模版代码。 
    <#@import namespace="System.Collections.Generic" #> 
    <# 
    Dictionary<string,Type> properties = new Dictionary<string,Type>(); 
    properties.Add("Age",typeof(int)); 
    properties.Add("Firstname", typeof(string)); 
    properties.Add("Surname", typeof(string)); 
    #> 
    using System;

    public class <#="MyClass"#>{ 
    <# foreach(string name in properties.Keys) { #> 
        public <#=properties[name].Name#> <#=name#>{ 
            get; set; 
        } 
    <# } #> 
    }

这些模版代码生成了一个类,并使用了一个名为"properties"的字典的每一个Key作为这个类的属性。

  • 保存".tt"文件。当你完成这一步时,不可思议的会自动出现一个依赖于模版的".cs"文件,其看来像下面这样:
    Entity Framework技巧系列之一 - Tip 1 - 5
     1 using System;
    2
    3 public class MyClass{
    4 public Int32 Age{
    5 get; set;
    6 }
    7 public String Firstname{
    8 get; set;
    9 }
    10 public String Surname{
    11 get; set;
    12 }
    13 }
    Entity Framework技巧系列之一 - Tip 1 - 5

正如你看到的,T4非常简单且对熟悉ASP.NET的使用者来说相当容易使用…

试一下吧。

提示4. 概念模式定义语言规则

第一个版本的Entity Framework在不久以前随着.NET 3.5 SP1一些发布了。

Entity Framework文档的一个最大漏洞是缺少一个对CSDL(概念模式描述语言)的正式文档描述。

人们想了解的CSDL是Entity Framework用来描述实体数据模型(EDM)的具体格式。

这些规则可以由嵌入System.Data.Entity.dll程序集资源中的XSD推到而出,但是一大些规则靠Entity Framework本身来限制。

如果你对这些规则的细节感兴趣,可以参见formal CSDL specification here。

这份文档主要针对Astoria所使用的CSDL1.1版本,但是CSDL1.0与1.1版本的大部分是相同的,这些差异都明确的列于这里

免责声明:我帮助编辑了这个文档的一些部分。如果你存在问题请告诉我。

提示5. 如何限制EF查询返回的类型

考虑你有一个如下这样的模型:

Entity Framework技巧系列之一 - Tip 1 - 5

怎样仅仅查询Cars?

 

这是OfType<SubType>()登场的时候。你编写如下这样的代码:

1 var onlyCars = from car in ctx.Vehicles.OfType<Car>()
2 select car;

且这个可以很好的工作。这将结果限制在Cars,附带包含了Cars,Sports

怎样仅查询Cars而不附带其子类型?

考虑你的预算只够选择一个简单的家用轿车。这意味着你不想要SUV与运动轿车(SportsCars)。

你需要在查询中明确限制以避免返回所有的子类型:

1 var onlyCars = from car in ctx.Vehicles.OfType<Car>()
2 where !(car is SportsCar) && !(car is SUV)
3 select car;

现在你的代码仅返回Cars类型的对象。

外传

这种解决方案唯一令人遗憾的是你不得不显示排除所有你可能不想要的子类型,在某些有多级继承或一级中有大量派生类的情况下可能会有很多子类。

如果下面的写法被支持将会更好(可惜这是不支持的):

1 var onlyCars = from car in ctx.Vehicles.OfType<Car>()
2 where car.GetType() == typeof(Car)
3 select car;

问题是你认为支持这种类型的查询对我们有多重要呢?