Entity Framework 6 Recipes 2nd Edition(9-2)译->用WCF更新单独分离的实体
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或是update的SQL语句。
在这个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更新单独分离的实体的更多相关文章
- Entity Framework 6 Recipes 2nd Edition 译 -> 目录 -持续更新
因为看了<Entity Framework 6 Recipes 2nd Edition>这本书前面8章的翻译,感谢china_fucan. 从第九章开始,我是边看边译的,没有通读,加之英语 ...
- Entity Framework 6 Recipes 2nd Edition(9-1)译->用Web Api更新单独分离的实体
第九章 在N层结构的应用程序中使用EF 不是所有的应用都能完全地写入到一个单个的过程中(就是驻留在一个单一的物理层中),实际上,在当今不断发展的网络世界,大量的应用程序的结构包含经典的表现层,应用程, ...
- Entity Framework 6 Recipes 2nd Edition(9-3)译->找出Web API中发生了什么变化
9-3. 找出Web API中发生了什么变化 问题 想通过基于REST的Web API服务对数据库进行插入,删除和修改对象图,而不必为每个实体类编写单独的更新方法. 此外, 用EF6的Code Fri ...
- Entity Framework 6 Recipes 2nd Edition(9-4)译->Web API 的客户端实现修改跟踪
9-4. Web API 的客户端实现修改跟踪 问题 我们想通过客户端更新实体类,调用基于REST的Web API 服务实现把一个对象图的插入.删除和修改等数据库操作.此外, 我们想通过EF6的Cod ...
- Entity Framework 6 Recipes 2nd Edition(13-2)译 -> 用实体键获取一个单独的实体
问题 不管你用DBFirst,ModelFirst或是CodeFirst的方式,你想用实体键获取一个单独的实体.在本例中,我们用CodeFirst的方式. 解决方案 假设你有一个模型表示一个Paint ...
- Entity Framework 6 Recipes 2nd Edition(13-3)译 -> 为一个只读的访问获取实体
问题 你想有效地获取只是用来显示不会更新的操作的实体.另外,你想用CodeFirst的方式来实现 解决方案 一个非常常见行为,尤其是网站,就是只是让用户浏览数据.大多数情况下,用户不会更新数据.在这种 ...
- Entity Framework 6 Recipes 2nd Edition(13-4)译 -> 有效地创建一个搜索查询
问题 你想用LINQ写一个搜索查询,能被转换成更有效率的SQL.另外,你想用EF的CodeFirst方式实现. 解决方案 假设你有如下Figure 13-6所示的模型 Figure 13-6. A s ...
- Entity Framework 6 Recipes 2nd Edition(13-5)译 -> 使POCO的修改追踪更高
问题 你正在使用POCO,你想提高修改跟踪的性能,同时使内存消耗更少.另外,你想通过EF的CodeFirst方式来实现. 解决方案 假设你有一个关于Account(帐户)和相关的Payments(支付 ...
- Entity Framework 6 Recipes 2nd Edition(13-9)译 -> 避免Include
问题 你想不用Include()方法,立即加载一下相关的集合,并想通过EF的CodeFirst方式实现. 解决方案 假设你有一个如Figure 13-14所示的模型: Figure 13-14. A ...
随机推荐
- 用dubbo时遇到的一个序列化的坑
首先,这是标题党,问题并不是出现在序列化上,这是报错的一部分: Caused by: com.alibaba.dubbo.remoting.RemotingException: Failed to s ...
- nginx+iis+redis+Task.MainForm构建分布式架构 之 (redis存储分布式共享的session及共享session运作流程)
本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,上一篇分享文章制作是在windows上使用的nginx,一般正式发布的时候是在linux来配 ...
- JDK安装与配置
JDK安装与配置 一.下载 JDK是ORACLE提供免费下载使用的,官网地址:https://www.oracle.com/index.html 一般选择Java SE版本即可,企业版的选择Java ...
- 《MySQL必知必会》学习笔记
数据库:数据库是一种以某种有组织的方式存储的数据集合.其本质就是一个容器,通常是一个或者一组文件. 表:表示一种结构化的文件,可用来存储某种特定类型的数据. 模式:描述数据库中特定的表以及整个数据库和 ...
- 2-1 Linux 操作系统及常用命令
根据马哥linux初级视频 2-1.2-2来编辑 1. GUI与CLI GUI: Graphic User Interface CLI: Command Line Interface 注:在Windo ...
- linux压力测试工具stress
最近给PASS平台添加autoscaling的功能,根据服务器的负载情况autoscaling,为了测试这项功能用到了stress这个压力测试工具,这个工具相当好用了.具体安装方式就不说了.记录下这个 ...
- TFS 生成发布代理
下载Agent 后,执行配置命令 参考 安装TFS(2015)工作组模式代理服务器(Agent)
- hibernate5.2需要的最少jar文件
hibernate5.2需要的最少jar文件: required文件夹中的所有jar文件 + mysql-connector-java-bin.jar.
- Vue.js——60分钟组件快速入门(下篇)
概述 上一篇我们重点介绍了组件的创建.注册和使用,熟练这几个步骤将有助于深入组件的开发.另外,在子组件中定义props,可以让父组件的数据传递下来,这就好比子组件告诉父组件:"嘿,老哥,我开 ...
- Hadoop学习笔记—22.Hadoop2.x环境搭建与配置
自从2015年花了2个多月时间把Hadoop1.x的学习教程学习了一遍,对Hadoop这个神奇的小象有了一个初步的了解,还对每次学习的内容进行了总结,也形成了我的一个博文系列<Hadoop学习笔 ...