9-2. 用WCF更新单独分离的实体

问题

你想通过WCF为一个数据存储发布查询,插入,删除和修改,并且使这些操作尽可能地简单

此外,你想通过Code First方式实现EF6的数据访问管理

解决方案

假设有如Figure 9-2所示模型.

Figure 9-2. 博客的posts(博文)和comments(评论)模型

模型表示博客的文章和评论之间的关系. 为了简化,我们去掉了很多属性,像作者,发文时间等。我们想把所有的数据库代码封装到WCF后,让客户端通过通过WCF读取,更新,删除,和插入Posts或Comments. 下面是创建WCF服务的步骤:

1. 创建一个新的Visual Studio 解决方案, 添加一个 c# 类库项目.并命名为Recipe2.

2. 在“Recipe1.Service ”项目中添加EF6的引用。最好是借助 NuGet 包管理器来添加。在”引用”上右击,选择”管理 NuGet 程序包.从“联机”标签页,定位并安装EF6包。这样将会下载,安装并配置好EF6库到你的项目中。

3. 向Recipe2项目添加三个类: Post, Comment, 和Recipe2Context. Post 和

Comment 用POCO 实体类,并直接遇到到Post 和 Comment 表. Recipe2Context是提供EF6服务的DbContext对象。确保WCF的实体类拥有

DataContract 和 DataMember 特性,如Listing 9-7所示.

Listing 9-7. POCO 类 Post, Comment,和 Recipe2Context

[DataContract(IsReference = true)]

public class Post

{

public Post()

{

Comments = new HashSet<Comment>();

}

[DataMember]

public int PostId { get; set; }

[DataMember]

public string Title { get; set; }

[DataMember]

public virtual ICollection<Comment> Comments { get; set; }

}

[DataContract(IsReference=true)]

public class Comment

{

[DataMember]

public int CommentId { get; set; }

[DataMember]

public int PostId { get; set; }

[DataMember]

public string CommentText { get; set; }

[DataMember]

public virtual Post Post { get; set; }

}

public class EFRecipesEntities : DbContext

{

public EFRecipesEntities()

: base("name= Recipe2ConnectionString")

{

}

public virtual DbSet<Post> Posts{get;set;}

public virtual DbSet<Comment> Comments{get;set;}

}

4. 向Recipe2项目中添加一个 App.config 文件,把如 Listing 9-8所示的连接字符串复制进去.

Listing 9-8.   Recipe2类库的连接字符串

<connectionStrings>

<add name="Recipe2ConnectionString"

connectionString="Data Source=.;

Initial Catalog=EFRecipes;

Integrated Security=True;

MultipleActiveResultSets=True"

providerName="System.Data.SqlClient" />

</connectionStrings>

5. 向解决方案中添加一个WCF服务应用程序. 使用默认名称Service1(译注:vs2013默认的是WcfService1)

. 用 Listing 9-9替换IService1.cs中的代码

Listing 9-9. The Service Contract for Our Service

[ServiceContract]

public interface IService1

{

[OperationContract]

void Cleanup();

[OperationContract]

Post GetPostByTitle(string title);

[OperationContract]

Post SubmitPost(Post post);

[OperationContract]

Comment SubmitComment(Comment comment);

[OperationContract]

void DeleteComment(Comment comment);

}

6. 用Listing 9-10里的代码替换 Service1.svc.cs 文件中的代码. 添加对Recipe2类库项目的引用,以便正确引用实体类. 添加EF6的引用.

Listing 9-10所示代码实现服务(确保项目引用了System.Data.Entity 和System.Security)

public class Service1 : IService

{

public void Cleanup()

{

using (var context = new EFRecipesEntities())

{

context.Database.ExecuteSqlCommand("delete from chapter9.comment");

context. Database.ExecuteSqlCommand ("delete from chapter9.post");

}

}

public Post GetPostByTitle(string title)

{

using (var context = new EFRecipesEntities())

{

context.Configuration.ProxyCreationEnabled = false;

var post = context.Posts.Include(p => p.Comments)

.Single(p => p.Title == title);

return post;

}

}

public Post SubmitPost(Post post)

{

using(var context=new EFRecipesEntities())//译者:添加

{                                                                         //

context.Entry(post).State =

//id=0表示添加,其它情况表示更新

post.PostId == 0 ? EntityState.Added : EntityState.Modified;

context.SaveChanges();

return post;

}//译者:添加

}

public Comment SubmitComment(Comment comment)

{

using (var context = new EFRecipesEntities())

{

context.Comments.Attach(comment);

if (comment.CommentId == 0)

{

// 插入

context.Entry(comment).State = EntityState.Added);

}

else

{

//设置单个属性状态为modified, 实体状态也会是 modified, 但只更新单个的属性,而不是整个实体

context.entry(comment).Property(x => x.CommentText).IsModified = true;

}

context.SaveChanges();

return comment;

}

}

public void DeleteComment(Comment comment)

{

using (var context = new EFRecipesEntities())

{

context.Entry(comment).State = EntityState.Deleted;

context.SaveChanges();

}

}

}

7.最后,添加一个windows控制台项目,运用它来测试WCF服务,拷贝Listing 9-11里的代码到Program类里,

. 右击控制台项目里的“引用”,选择“添加服务引用”, 并选择Service1 服务(译注:在添加引用前,最好先生成一次服务,不然可能出现引用不了的情况)

. 还需要添加第一步创建的Recipe2类库项目.

Listing 9-11. Our Windows Console Application That Serves as Our Test Client

class Program

{

static void Main(string[] args)

{

using (var client = new ServiceReference2.Service1Client())

{

//清除之前的数据

client.Cleanup();

//插入一个Post

var post = new Post { Title = "POCO Proxies" };

post = client.SubmitPost(post);

// 更新这个 post

post.Title = "Change Tracking Proxies";

client.SubmitPost(post);

// 添加一个 comment

var comment1 = new Comment {

CommentText = "Virtual Properties are cool!",

PostId = post.PostId };

var comment2 = new Comment {

CommentText = "I use ICollection<T> all the time",

PostId = post.PostId };

comment1 = client.SubmitComment(comment1);

comment2 = client.SubmitComment(comment2);

// 更新一个 comment

comment1.CommentText = "How do I use ICollection<T>?";

client.SubmitComment(comment1);

// 删除comment 1

client.DeleteComment(comment1);

//获取 posts的所有 comments

var p = client.GetPostByTitle("Change Tracking Proxies");

Console.WriteLine("Comments for post: {0}", p.Title);

foreach (var comment in p.Comments)

{

Console.WriteLine("\tComment: {0}", comment.CommentText);

}

}

}

}

译注:有两个地方一定要注意,1,Post,Comment类与数据库的映射以及它们之间的关系,上述代码中并未列出。2,需要把步骤4列出的连接字符串复制到Service1项目的web.config里.另:仅从9-1和9-2来看,发现原书有不少错误,甚至是代码上的,有些地方我直接修改过来,也没特别作说明。

控制台输出的结果如下面的 Listing 9-11所示:

========================================================

Comments for post: Change Tracking Proxies

Comment: I use ICollection<T> all the time

========================================================

它是如何工作的?

启动我们用来测试WCF服务的控制台项目,,创建一个WCF服务实例,因为使用using{}块,所以可以确保代码执行出此块,会立即调用Dispose()。防止引起异常,我们调用Cleanup()方法,先把数据库里已有的数据删除,接下来两行代码,我们调用服务的SubmitPost()方法,(这个方法的实现查看Listing 9-10),该方法里先判断PostId的值,如果为0,表示是一个新添加的Post对象,并把它的状态设置为Added. 否则,它就是一个已存在于数据库的Post对象,我们要对它进行更新操作,因为把它的状态设置Modified. 尽管有几分粗糙,但这种方法能判定Post对象状态(是新的还是已存在的),相比ID依赖于整型在运行时初始值为0,一个更好的方法是,传递一个额外的参数到SubmitPost()方法,或是创建一个单独的InsertPost()方法。

最佳实践:如果要插入这个post, 把它的state设置成EntityState.Added. 否则, 把它的state设置成EntityState.Modified. 根据EntityState值会产生一条Insert或是updateSQL语句。

在这个post对象插入后,该对象的PostId会变更成正确的值.

插入Comment或更新Comment一个单独的属性类似于插入或更新post,但有一个很在的不同:我们的业务规则是只更新comment的CommentText 属性.该属性包含comment的主体, 且我们不想更新其它属性.因此,我们给CommentText 作上 modified标志.EF将会生成一个简单的只更新CommentText列的Sql语句.如果我们修改Comment多个属性,我们就需要想办法跟踪到哪个属性在客户端被修改了. 但是用这种方式我们就不需要在客户端用一个复杂的方法来跟踪实体的哪个属性发生变化。

为了删除一个 comment,我们调用context 对象的Entity() 方法, 并传递一个state被设置成Deleted 的 comment 作为参数, 因为comment 被设置为Deleted,所以保存时会生成一条delete的SQL语句。

最后, GetPostByTitle() 为每个符合条件的Post预先加载所有,以post和相关的comments对象图的形式返回. 因为我们应用了POCO类, 我们想要EF返回一个包含post和comment类的动态代理对象.可惜的是, WCF 不能序列化一个代理对象. 然而我们把 ProxyCreationEnabled 设置成false, 就可以简单的使代理类不质疑EF实际返回的对象。如果我们企图序列这个代理对象,会收到如下的错误信息:

基础连接已经关闭: 服务器关闭了本应保持活动状态的连接(译注:测试过)。我们甚至可以在构造函数中使用  ProxyCreationEnabled = false,迫使它在所有对所有服务方法有效果

在本节, 我们通过WCF利用POCO对象进行CRUD操作. 由于没有保存客户端的状态信息,我们只能分别创建插入,修改,删除post和comment的方法。

.在本章的其它节里我们将减少服务端的方法来简化客户端与服务器的通信。

附:创建示例用到的数据库的脚本文件

Entity Framework 6 Recipes 2nd Edition(9-2)译->用WCF更新单独分离的实体的更多相关文章

  1. Entity Framework 6 Recipes 2nd Edition 译 -> 目录 -持续更新

    因为看了<Entity Framework 6 Recipes 2nd Edition>这本书前面8章的翻译,感谢china_fucan. 从第九章开始,我是边看边译的,没有通读,加之英语 ...

  2. Entity Framework 6 Recipes 2nd Edition(9-1)译->用Web Api更新单独分离的实体

    第九章 在N层结构的应用程序中使用EF 不是所有的应用都能完全地写入到一个单个的过程中(就是驻留在一个单一的物理层中),实际上,在当今不断发展的网络世界,大量的应用程序的结构包含经典的表现层,应用程, ...

  3. Entity Framework 6 Recipes 2nd Edition(9-3)译->找出Web API中发生了什么变化

    9-3. 找出Web API中发生了什么变化 问题 想通过基于REST的Web API服务对数据库进行插入,删除和修改对象图,而不必为每个实体类编写单独的更新方法. 此外, 用EF6的Code Fri ...

  4. Entity Framework 6 Recipes 2nd Edition(9-4)译->Web API 的客户端实现修改跟踪

    9-4. Web API 的客户端实现修改跟踪 问题 我们想通过客户端更新实体类,调用基于REST的Web API 服务实现把一个对象图的插入.删除和修改等数据库操作.此外, 我们想通过EF6的Cod ...

  5. Entity Framework 6 Recipes 2nd Edition(13-2)译 -> 用实体键获取一个单独的实体

    问题 不管你用DBFirst,ModelFirst或是CodeFirst的方式,你想用实体键获取一个单独的实体.在本例中,我们用CodeFirst的方式. 解决方案 假设你有一个模型表示一个Paint ...

  6. Entity Framework 6 Recipes 2nd Edition(13-3)译 -> 为一个只读的访问获取实体

    问题 你想有效地获取只是用来显示不会更新的操作的实体.另外,你想用CodeFirst的方式来实现 解决方案 一个非常常见行为,尤其是网站,就是只是让用户浏览数据.大多数情况下,用户不会更新数据.在这种 ...

  7. Entity Framework 6 Recipes 2nd Edition(13-4)译 -> 有效地创建一个搜索查询

    问题 你想用LINQ写一个搜索查询,能被转换成更有效率的SQL.另外,你想用EF的CodeFirst方式实现. 解决方案 假设你有如下Figure 13-6所示的模型 Figure 13-6. A s ...

  8. Entity Framework 6 Recipes 2nd Edition(13-5)译 -> 使POCO的修改追踪更高

    问题 你正在使用POCO,你想提高修改跟踪的性能,同时使内存消耗更少.另外,你想通过EF的CodeFirst方式来实现. 解决方案 假设你有一个关于Account(帐户)和相关的Payments(支付 ...

  9. Entity Framework 6 Recipes 2nd Edition(13-9)译 -> 避免Include

    问题 你想不用Include()方法,立即加载一下相关的集合,并想通过EF的CodeFirst方式实现. 解决方案 假设你有一个如Figure 13-14所示的模型: Figure 13-14. A ...

随机推荐

  1. C++中的变长参数

    新参与的项目中,为了使用共享内存和自定义内存池,我们自己定义了MemNew函数,且在函数内部对于非pod类型自动执行构造函数.在需要的地方调用自定义的MemNew函数.这样就带来一个问题,使用stl的 ...

  2. const extern static 终极指南

    const extern static 终极指南 不管是从事哪种语言的开发工作,const extern static 这三个关键字的用法和原理都是我们必须明白的.本文将对此做出非常详细的讲解. co ...

  3. Jvm 内存浅析 及 GC个人学习总结

    从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C ...

  4. Log4net - 项目使用的一个简单Demo

    参考页面: http://www.yuanjiaocheng.net/entity/entitytypes.html http://www.yuanjiaocheng.net/entity/entit ...

  5. 通过sails和阿里大于实现短信验证

    通过sails与阿里大于来实现注册短信验证码的发送,逻辑图如下 1.用户在客户端发送手机号给服务器,服务器接收到手机号,生成对应时间戳,随机四位数验证码 2.服务器将电话号码和验证码告诉阿里大于服务器 ...

  6. Linux 利用Google Authenticator实现ssh登录双因素认证

    1.介绍 双因素认证:双因素身份认证就是通过你所知道再加上你所能拥有的这二个要素组合到一起才能发挥作用的身份认证系统.双因素认证是一种采用时间同步技术的系统,采用了基于时间.事件和密钥三变量而产生的一 ...

  7. 什么是英特尔® Edison 模块?

    英特尔® Edison 模块 是一种 SD 卡大小的微型计算芯片,专为构建物联网 (IoT) 和可穿戴计算产品而设计. Edison 模块内含一个高速的双核处理单元.集成 Wi-Fi*.蓝牙* 低能耗 ...

  8. 【腾讯Bugly干货分享】移动客户端中高效使用SQLite

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57b57f2a0703f7d31b9a3932 作者:赵丰 导语 iOS 程序能 ...

  9. 吸顶大法 -- UWP中的工具栏吸顶的实现方式之一

    如果一个页面中有很长的列表/内容,很多应用都会在用户向下滚动时隐藏页面的头,给用户留出更多的阅读空间,同时提供一个方便的吸顶工具栏,比如淘宝中的店铺页面. 下面是一个比较简单的实现,如果有同学有更好的 ...

  10. 从Maya中把模型搬运至网页的过程

    虽然利用threejs来在网页中渲染3d模型不是第一次折腾了,但是还是遇到了各种问题.总结下我所遇到的问题,希望能给正在使用threejs的小伙伴一个帮助. 一.所使用的软件与开发环境 Maya201 ...