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> Products { get; }
5 }

这告诉CodeOnly使用2个EntitySet,一个称作CategoriesCategory实体的集合与一个称作ProductsProduct实体的集合,来引导一个模型。

然后如果需要你将来可以通过操作ContextBuilder来改进这个模型。

问题:

但是如果你没有一个强类型的Context类呢?

如果你决定在运行时需要一个模型,但没有一个适合的强类型Context类可以使用。

在今天早些时候一个客户恰好问我这个问题。

解决方案:

最终你可以直接使用ObjectContext。当你通过Code-Only做这个工作时对模型一无所知。但是这不是那么糟糕,所有你需要做的就是明确的告诉Code-Only通常可以由强类型context了解到的所有信息。

例如这个:

1 public ObjectSet<Person> People { get; }

可以被转换为这个:

1 var builder = new ContextBuilder<ObjectContext>();
2 builder.RegisterSet<Person>(“People”);

有趣吗?

结尾的例子:

这个例子,演示完全不使用强类型ObjectContext下将一个Person对象(BillG)存储到数据库并再次检索出来:

首先是Person类(这是个POCO):

1 public class Person
2 {
3     public int ID { get; set; }
4     public string Firstname { get; set; }
5     public string Surname { get; set; }
6 }

接下来是建立ObjectContext的代码:

 1 // Create the contextbuilder, and tell it about the People set.
 2 var builder = new ContextBuilder<ObjectContext>();
 3 builder.RegisterSet<Person>("People");
 4 // Create a connection
 5 string connstr = @"Data Source=.\SQLEXPRESS;Initial Catalog=PeopleDb;Integrated Security=True;Pooling=False;MultipleActiveResultSets=True";
 6 var conn = new SqlConnection(connstr);
 7
 8 // Create an ObjectContext from the builder
 9 using (ObjectContext ctx = builder.Create(conn))
10 {
11     // Create the database if it doesn’t already exist
12     if (!ctx.DatabaseExists())
13        ctx.CreateDatabase();
14
15     // Create Bill
16     Person p = new Person {
17         ID = 1,
18         Firstname = "Bill",
19         Surname = "Gates"
20     };
21
22     // Add Bill to the context
23     // UPDATE: thanks to danny for the simplification
24     ctx.CreateObjectSet<Person>().AddObject(p);
25  using the general purpose
26     // AddObject method.
27     // The only tricky part is the EntitySet name with must
28     // be qualified with the the container name,
29     // in this case is ObjectContext.
30     ctx.AddObject("ObjectContext.People", p);
31     ctx.SaveChanges();
32     // Issue a query against the People set.
33     var bill = (from person in ctx.CreateObjectSet<Person>()
34                 where person.Firstname == "Bill"
35                 select person).Single();
36     // Make and Save a change.
37     bill.Firstname = "William";
38     ctx.SaveChanges();
39 }

就不是强类型的而论这非常简单了。

提示43. 怎样通过一个Data Service的验证

问题:

当我们编写访问一个Data Service的代码,如SharePoint,客户端程序必须提供一系列有效的凭据,否则你将收到一个”401 Unauthorized”的响应。

针对与DataServie宿主在同一网站上的Silverlight应用,通常会自动处理好这个问题。

但对于如WPF应用,这需要手动处理。

解决方案:

这个解决方案在你执行任何查询或更新之前在DataServieContext上设置凭证属性,像这样:

1 Uri uri = new Uri("http://mflasko-dev/_vti_bin/listdata.svc"));
2 TeamSiteDataContext ctx = new TeamSiteDataContext(uri);
3 ctx.Credentials = System.Net.CredentialCache.DefaultCredentials;
4 …

就是这样。

当然如果需要你可以提供另一套凭证信息,但是一般来说,DefaultCredentials就是你想要的。

提示44. 怎样导航到一个Odata兼容服务

最近我做了一个关于Data Service与Odata的速成课程。

在这个过程中我发现我的笔记可能对大家有用。

这里给出我的备忘单供你快速掌握Odata Url。

注意:Odata服务可能未必完全支持下面这些特性:但是如果它们支持,这就是你使用它们的方法。

服务:

所有服务由一个宿主于某一地方的Data Service开始:

http://server/service.svc

基本查询:

通过资源集(resource set)来访问Data Service实体,像这样:

http://server/service.svc/People

使用主键来请求一个指定的实体,像这样:

http://server/service.svc/People(16)

或通过使用一个到你知道的对象的关联引用:

http://server/service.svc/People(16)/Mother

这请求16号人的母亲。

一旦你可以识别出一个实体,你可以直接导航到它的属性:

http://server/service.svc/People(16)/Mother/Firstname

$value:

但是上一个查询将属性值包在XML中,如果你仅想要单纯的属性值,可以像这样在url后面附加一个$value:

http://server/service.svc/People(16)/Mother/Firstname/$value

$filter:

你可以使用$filter来过滤资源集:

http://server/service.svc/People?$filter=Firstname  eq ‘Fred’

注意过滤条件字符串中使用单引号。

而数字不需要引号:

http://server/service.svc/Posts?$filter=AuthorId eq 1

要按时间过滤你需要在过滤条件中标识出时间,如下:

http://server/service.svc/Posts?$filter=CreatedDate eq DateTime‘2009-10-31‘

也可以通过关联引用来过滤:

http://server/service.svc/People?$filter=Mother/Firstname eq ‘Wendy‘

你可以在过滤条件中使用的基本运算符:


Operator


Description


C# equivalent


eq


equals


==


ne


not equal


!=


gt


greater than


>


ge


greater than or equal


>=


lt


less than


<


le


less than or equal


<=


and


and


&&


or


or


||


()


grouping


()

如果需要还有一系列可以在过滤条件中使用的函数。

$expand:

如果你想在结果中包含相关项目,像这样使用$expand:

http://server/service.svc/Blogs?$expand=Posts

这将返回匹配的Blogs及每个Blog的post。

$select:

一些Data Service允许你将结果限制在仅你需要的属性 – 又称投影 – 例如如果你仅想要匹配Post的Id与Title,你需要如下这样的Url:

http://server/service.svc/Posts?$select=Id,Title

你甚至也可以映射到关联对象的属性,如这样:

http://server/service.svc/Posts?$expand=Blog&$select=Id,Title,Blog/Name

这仅映射到了每个Post的 Id,Title与Blog的名称。

$count:

如果你仅想知道将返回多少条记录,而不检索它们,你需要使用$count:

http://server/service.svc/Blogs/$count

注意$count成为URL的片段之一-它不是查询字符串的一部分-所以如果你想与另一个操作如$filter合并,你需要将$count放在前面,像这样:

http://server/service.svc/Posts/$count?$filter=AuthorId eq 6

这个查询返回作者是6号person的帖子的数量。

$orderby:

如果你需要结果是排序过的,可以使用$orderby:

http://server/service.svc/Blogs?$orderby=Name

返回的结果是升序排序,要进行降序排序你需要:

http://server/service.svc/Blogs?$orderby=Name%20desc

要首先按一个属性排序,然后按另一个属性排序,你需要:

http://server/service.svc/People?$orderby=Surname,Firstname

如果需要也可以在其中加入desc。

$top:

如果你仅想要前10条项目,可以像这样使用$top:

http://server/service.svc/People?$top=10

$skip

如果你仅对某一页数的数据感兴趣,你需要联合使用$top与$skip:

这告诉Data Service跳过前20条匹配结果并返回下10条数据。当每页有10个项而你需要显示第3页结果时这很有用。

注意:通常将$top&$skip与$orderby合并使用是一个好主意,以保证由底层数据源检索到的结果的顺序是一致的。

$inlinecount&$skiptoken:

使用$top与$skip允许客户端控制分页。

但是服务器也需要一种方法来控制分页-以最小化服务初级用户及那些恶意用户所需的负载-OData协议通过Server Driven Paging(服务器驱动的分页)来完成这个任务。

当Server Driven Paging开启后,即使客户端请求所有记录,但它们将只会得到一个页的结果。

正如你想象,这会给客户端应用开发人员带来一点麻烦。

如果客户端需要知道到底有多少结果,它们可以在查询后追加$inlinecount选项,如下:

http://server/service.svc/People?$inlinecount=allpages

结果将会“内联”包含一个总数,及一个服务器生成的用于获取下一页结果的url。

这个服务器生成的url包含一个$skiptoken,这与一个游标或书签等效,以通知服务器由哪重新开始:

http://server/service.svc/People?$skiptoken=4

$links

有时候你只需得到与实体关联的一个特殊实体的url,这时候就用到$links了:

http://server/service.svc/Blogs(1)/$links/Posts

这告诉Data Service返回Blog1相关的所有Posts的链接-即url。

$metadata

如果你需要知道一个Odata兼容的数据服务暴露的模型的信息,你可以通过在服务的根路径后面拼接一个$metadata选项,如下:

http://server/service.svc/$metadata

这将返回一个Data Service暴露的包含概念模型(又称EDM)的EDXM文件。

总结

这会帮助你早日上手。

我将更多的探索Data Service与Odata,我也将与你分享我的所学,请锁定我的博客。

提示45. 怎样在运行时置换EF元数据

背景:

默认情况下Entity Framework将它的元数据作为一个资源嵌入程序集中。

同时它也将一个引用了这些资源连接字符串放入App或Web配置文件中,像这样:

1 <add name="BloggingEntities" connectionString="metadata=res://*/Blogging.csdl|res://*/Blogging.ssdl|res://*/Blogging.msl;provider=System.Data.SqlClient;
2 provider connection string=&quot;Data Source=.\SQLEXPRESS;Initial Catalog=TipsDatabase;Integrated Security=True;MultipleActiveResultSets=True&quot;"
3 providerName="System.Data.EntityClient" />

这使其可以很容易的开始:

1 using (BloggingEntities ctx = new BloggingEntities())
2 {

注意:使用res://*告诉EF在程序集资源内部查找各种各样的元数据。

问题

但是将元数据嵌入作为资源同时意味着它本质上是不可改变的。

假如你需要在运行时更改它怎么办呢?

有很多原因是你想要在运行时更改元数据,但是也许最可能的原因是在生产环境你不得不配合有一套不同数据库设计想法的DBA。

一般情况下这意味着你不得不由于数据库的更改来更改一部分存储模型(SSDL)与映射(MSL)。

这样做完全可以,事实上这是使用Entity Framework的一大好处,只要概念模型或者称CSDL—开发的程序所依赖的东西—保持不变。

解决方案

所以怎样在运行时插入一个不同的MSL与SSDL呢?

下面是相关的步骤:

步骤1:

得到’元数据项目’:

1. 在设计器画布上右击,点击属性:

2. 将’元数据项目处理’设置为’ 复制到输出目录’:

3. 生成项目,查看bin\debug(或bin\release)目录:

步骤2:

现在你有了文件形式的CSDL/MSL/SSDL,很容易替换其中一个或多个(一般是SSDL与MSL)为与你环境匹配的版本。所以你可以有两套,其一用于开发环境,另一套用于生成环境。

然后你需要做的仅是修改你的连接字符串来指向正确的文件集,类似下面这样:

 1 var connStr =
 2 @"metadata=.\Blogging.csdl|.\Production.ssdl|.\Production.msl;
 3     provider=System.Data.SqlClient;
 4     provider connection string=""
 5            Data Source=.\SQLEXPRESS;
 6            Initial Catalog=TipsDatabase;
 7            Integrated Security=True;
 8            MultipleActiveResultSets=True
 9    """;
10
11 using (BloggingEntities ctx = new BloggingEntities(connStr))
12 {

注意现在使用的连接字符串不是res://而是.\,这告知EF在与应用程序相同的目录下查找元数据。

事情就是这样。

时间: 2024-08-27 00:08:47

Entity Framework技巧系列之十一 - Tip 42 - 45的相关文章

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

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

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

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

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

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

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

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

Entity Framework技巧系列之九 - Tip 35 - 36

提示35. 怎样实现OfTypeOnly<TEntity>()这样的写法 如果你编写这样LINQ to Entities查询: 1 var results = from c in ctx.Vehicles.OfType<Car>() 2 select c; 这会返回,Cars包括那些派生自Car类型,如SportCar或SUV类型的汽车. 如果你仅想要Cars即不想要如SportCar或SUV等派生类型汽车,你会在LINQ to Objects中这样写: 1 var results

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

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

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