提示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在与应用程序相同的目录下查找元数据。

事情就是这样。

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

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

    提示51. 怎样由任意形式的流中加载EF元数据 在提示45中我展示了怎样在运行时生成一个连接字符串,这相当漂亮. 其问题在于它依赖于元数据文件(.csdl .ssdl .msl)存在于本地磁盘上. 但 ...

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

    提示37. 怎样进行按条件包含(Conditional Include) 问题 几天前有人在StackOverflow上询问怎样进行按条件包含. 他们打算查询一些实体(比方说Movies),并且希望预 ...

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

    提示37. 怎样进行按条件包含(Conditional Include) 问题 几天前有人在StackOverflow上询问怎样进行按条件包含. 他们打算查询一些实体(比方说Movies),并且希望预 ...

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

    提示6. 如何及何时使用贪婪加载 什么时候你需要使用贪婪加载? 通常在你的程序中你知道对查询到的实体将要进行怎样的操作. 例如,如果你查询一个订单以便为一个客户重新打印,你知道没有组成订单的项目即产品 ...

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

    提示35. 怎样实现OfTypeOnly<TEntity>()这样的写法 如果你编写这样LINQ to Entities查询: 1 var results = from c in ctx. ...

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

    提示16. 当前如何模拟.NET 4.0的ObjectSet<T> 背景: 当前要成为一名EF的高级用户,你确实需要熟悉EntitySet.例如,你需要理解EntitySet以便使用 At ...

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

    提示20. 怎样处理固定长度的主键 这是正在进行中的Entity Framework提示系列的第20篇. 固定长度字段填充: 如果你的数据库中有一个固定长度的列,例如像NCHAR(10)类型的列,当你 ...

  8. Entity Framework技巧系列之十四 - Tip 56

    提示56. 使用反射提供程序编写一个OData Service 在TechEd我收到一大堆有关将数据作为OData暴露的问题. 到目前为止你大概知道可以使用数据服务与Entity Framework将 ...

  9. Entity Framework技巧系列之十二 - Tip 46 - 50

    提示46. 怎样使用Code-Only排除一个属性  这次是一个真正简单的问题,由StackOverflow上这个问题引出.  问题:  当我们使用Code-Only把一个类的信息告诉Entity F ...

随机推荐

  1. history对象 back() forward() go() 和pushState() replaceState()

    History(Window.history对象)对象保存着用户上网的历史记录.处于安全方面的考虑,开发人员无法得知用户浏览过的URL,但是借由用户访问过的页面列表,同样可以在不知道实际URL的情况下 ...

  2. oracle中110个常用函数介绍

    1. ASCII 返回与指定的字符对应的十进制数; SQL> select ascii(A) A,ascii(a) a,ascii(0) zero,ascii( ) space from dua ...

  3. (十)foreac遍历、break和countinue以及标签和switch循环

    foreach语法,表示不必创建int变量去对由访问项构成的序列进行计数,foreach将自动产生每一项. 例:输出数组的所有元素. float f[]=new float[5]; f[0]=1.0f ...

  4. SLF4J 使用简记

    SLF4J 使用简记 使用 SLF4J有一段时间了,在此作上些许记录,以作提示. 本文使用的实际实现的日志框架是 Log4j,所以使用 log4j.properties 文件 SLF4J 需要引入的j ...

  5. express学习点滴- 永远不要忘记异步

    直接上两段代码,因为nodejs基于异步和事件回调的解决方式,涉及到异步的时候,问题往往藏得很深,以下这个简单的问题困扰了很久.之前怀疑是各种问题,到处改.直到最后一步一步跟代码,跟操作数据库部分豁然 ...

  6. MVC运行机制

    一,第一次程序运行时 1,第一次请求的时候 会获取配置文件,然后有个应用启动事件到global.asax.2,在Global.asax文件中,网站第一次运行会创建RouteTable对象,实现URL到 ...

  7. java 操作redis

    使用Java操作Redis需要jedis-2.1.0.jar,如果需要使用Redis连接池的话,还需commons-pool-1.5.4.jar package com.test; import ja ...

  8. 14.hibernate的反向生成实现全套增删改查

    图片顺序就是步骤顺序 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18.

  9. Union、Union All、Except、InterSect的区别

    UNION: 将多个「结果集 (result set)」的「行 (row)」合并,作为单个结果集返回,并移除重复的行.若有重复的行,只留下一个. UNION ALL: 将多个「结果集 (result ...

  10. 各硬件设备在Linux中的文件名