我的WCF4 Rest Service及Entity Framework with POCO之旅(三)——用Entity Framework和POCO Template实现数据模型及存储
既然这个系列标题中都含有Entity Framework和POCO,这两者若到第三篇还不出现那就太奇怪了。本文将介绍如何使用Entity Framework和POCO来实现数据模型的创建以及数据存储。
接着上文,我们现在已经有了一个微博服务。但是,一旦网站重启或者IIS重启,我们就会丢失之前所有发布的微博,这样的服务显然不是我们所能接受的,我们接下来要构建服务的数据存储。使用Entity Framework的Model-First设计方式,可以大大简化这个过程。
使用Entity Data Model创建数据模型
我们首先创建一个类库项目WcfRestServiceDemo.Data,然后添加新项“ADO.NET Entity Data Model”:
选择数据源,如果有现成的数据源的话可以从数据库导入。我们选择Empty model, 新创建数据模型。
从工具箱中找到Entity,拖入设计器,参考之前创建的数据类型,修改得到:
到这里数据模型就创建完了。下面我们要把它同步到SQL Server。
由模型生成数据库
到目前为止,模型还只存在C#项目中,虽然我们已经可以写代码来操纵数据,但实际上是无法运行的(或者说一运行就会挂掉的)。
要将模型同步到SQL Server,对设计器视图点右键,选择Generate Database from Model…
打开WcfRestServiceDemo.edmx.sql,右键选择Execute SQL,成功后模型就同步到SQL Server了。
修改服务代码
现在数据模型和数据存储都做好了,我们要修改服务代码来使用新的数据模型。
删去Microblog.cs,添加对WcfRestServiceDemo.Data和System.Data.Entity的引用,并修改MicroblogService.cs代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.ServiceModel.Activation; using System.ServiceModel.Web; using WcfRestServiceDemo.Data; namespace WcfRestServiceDemo.Service { [ServiceContract(Namespace = "WcfRestServiceDemo" )] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public class MicroblogService { [WebGet(UriTemplate = "" )] public List<Microblog> GetCollection() { using ( var container = new WcfRestServiceDemoContainer()) { return container.Microblogs.ToList(); } } [WebInvoke(UriTemplate = "" , Method = "POST" )] public Microblog Create(Microblog microblog) { microblog.PublishTime = DateTime.Now; using ( var container = new WcfRestServiceDemoContainer()) { container.Microblogs.AddObject(microblog); container.SaveChanges(); } return microblog; } [WebGet(UriTemplate = "{id}" )] public Microblog Get( string id) { using ( var container = new WcfRestServiceDemoContainer()) { return container.Microblogs.FirstOrDefault(m => m.Id == int .Parse(id)); } } [WebInvoke(UriTemplate = "{id}" , Method = "DELETE" )] public void Delete( string id) { using ( var container = new WcfRestServiceDemoContainer()) { container.Microblogs.DeleteObject( container.Microblogs.First(m => m.Id == int .Parse(id))); container.SaveChanges(); } } } } |
要让Entity Framework正常工作,我们还需要把它生成的app.config文件中的ConnectionString复制到服务的web.config文件中:
1 | < add name="WcfRestServiceDemoContainer" connectionString="metadata=res://*/WcfRestServiceDemo.csdl|res://*/WcfRestServiceDemo.ssdl|res://*/WcfRestServiceDemo.msl;provider=System.Data.SqlClient;provider connection string="Data Source=.;Initial Catalog=WcfRestServiceDemo;Integrated Security=True;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient" /> |
这里附带一提,有的时候我们会遇到无法正确加载Model的情形,这时只要将上面那个连接字符串中的*替换成数据模型(*.edmx)所在的程序集名称(这里是WcfRestServiceDemo.Data),显式指定搜索路径即可。
好,都改好了,现在测试一下服务,我先手工往数据库中插入一条记录,然后访问
http://localhost:6421/microblogs/:
怎么回事?Content, Id, PublishTime这些属性倒是一个也不少,但是也多了很多其他的东西。这是因为与POCO不同,基于EntityObject的Entity Data Model支持更改提醒、关系管理等许多其他功能,所以会包含更多的属性,只不过在我们这个应用场景中,并不需要这些支持。另一个问题是,这时默认的命名空间变成了
xmlns=http://schemas.datacontract.org/2004/07/WcfRestServiceDemo.Data
而且由于Microblog这个DataModel是自动生成的,我没有办法改它的DataContractAttribute(改了下次生成又会变回来):
1 2 3 4 5 6 7 | [EdmEntityTypeAttribute(NamespaceName= "WcfRestServiceDemo" , Name= "Microblog" )] [Serializable()] [DataContractAttribute(IsReference= true )] public partial class Microblog : EntityObject { … } |
要解决上述问题,我们该请出下一位主角POCO了。
使用ADO.NET C# POCO Entity Generator生成POCO模型
POCO全称Plain-Old CLR Objects(中文不知道怎么说)。以前有很多人手写POCO模型,不过现在已经有了一个非常强大的基于T4模板的模型生成器。
打开Visual Studio 2010的Extension Manager,找到ADO.NET C# POCO Entity Generator并安装(或者在这里下载):
然后打开WcfRestServiceDemo.edmx,右键选择Add Code Generation Item…:
Model.Context.tt和Model.tt分别是Container和Entity的T4模板。现在再运行一下服务,获取所有微博:
为什么会Empty Response? 调试一下服务,发现返回的类型不是Microblog, 而是System.Data.Entity.DynamicProxies.Microblog_
C60A574FC06ABEF4672858332CE687DDE70D188AD3BD73ED2731E8854D30C927,而WCF并不认识这个类型。这是因为默认的Entity Data Model启用了LazyLoading和Proxy机制的缘故。这两个特性我以后也许会详细讲,不过目前我们只要把它们关闭就好。虽然Model设计器提供了下面这个属性可以修改:
但是当我改成False然后对Model.Context.tt右键选择Run Custom Tool 重新生成Container代码,却没有任何效果,Container构造器代码依旧如下:
如果有哪位高手知道应该如何设置,请告诉我。我们现在先通过扩展Container类的方法来解决这个问题,下一篇讲如何通过修改T4模板来改变生成的Container。
在WcfRestServiceDemo.Data中创建WcfRestServiceDemoContainer.cs,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | using System; namespace WcfRestServiceDemo.Data { partial class WcfRestServiceDemoContainer { public static void Go(Action<WcfRestServiceDemoContainer> todo, bool isLazy = false ) { Go< object >(entities => { todo(entities); return null ; }, isLazy); } public static void GoAndSave(Action<WcfRestServiceDemoContainer> todo, bool isLazy = false ) { Go< object >(entities => { todo(entities); entities.SaveChanges(); return null ; }, isLazy); } public static T Go<T>(Func<WcfRestServiceDemoContainer, T> todo, bool isLazy = false ) { using ( var entities = new WcfRestServiceDemoContainer()) { entities.ContextOptions.LazyLoadingEnabled = isLazy; entities.ContextOptions.ProxyCreationEnabled = false ; return todo(entities); } } public static T GoAndSave<T>(Func<WcfRestServiceDemoContainer, T> todo, bool isLazy = false ) { return Go(entities => { var result = todo(entities); entities.SaveChanges(); return result; }, isLazy); } } } |
这里我顺便写了几个帮助方法,因为每次使用Container时写using很麻烦,关键是我经常改完数据忘记调SaveChanges().
最后修改MicroblogService.cs代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.ServiceModel.Activation; using System.ServiceModel.Web; using WcfRestServiceDemo.Data; namespace WcfRestServiceDemo.Service { [ServiceContract(Namespace = "WcfRestServiceDemo" )] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public class MicroblogService { [WebGet(UriTemplate = "" )] public List<Microblog> GetCollection() { return WcfRestServiceDemoContainer.Go(container => container.Microblogs.ToList()); } [WebInvoke(UriTemplate = "" , Method = "POST" )] public Microblog Create(Microblog microblog) { microblog.PublishTime = DateTime.Now; WcfRestServiceDemoContainer.GoAndSave(container => container.Microblogs.AddObject(microblog)); return microblog; } [WebGet(UriTemplate = "{id}" )] public Microblog Get( string id) { return WcfRestServiceDemoContainer.Go(container => container.Microblogs.FirstOrDefault(m => m.Id == int .Parse(id))); } [WebInvoke(UriTemplate = "{id}" , Method = "DELETE" )] public void Delete( string id) { WcfRestServiceDemoContainer.GoAndSave(container => container.Microblogs.DeleteObject( container.Microblogs.First(m => m.Id == int .Parse(id)))); } } } |
再次访问http://localhost:6421/microblogs/:
啊哈,又回到最初干净的状态了。只是Namespace问题以及属性名称的大小写问题还是没有解决,但是我们已经离目标更近一步了,我将在下一章讨论这些。
小结
本文介绍了:
- 如何用Entity Framework的Model-First方式构建数据模型及数据存储
- 如何改用POCO实现数据模型
- 如何关闭LazyLoading和ProxyCreation
- 以及一点点代码的sugar
我的WCF4 REST Service及Entity Framework with POCO之旅系列
- 我的WCF4 REST Service及Entity Framework with POCO之旅(一)——创建一个基本的RESTful Service
- 我的WCF4 REST Service及Entity Framework with POCO之旅(二)——选择请求/返回格式
- 我的WCF4 REST Service及Entity Framework with POCO之旅(三)——用Entity Framework和POCO Template实现数据模型及存储
- 我的WCF4 REST Service及Entity Framework with POCO之旅(四)——定制Entity
- 我的WCF4 REST Service及Entity Framework with POCO之旅(五)——身份验证
posted on 2011-03-31 09:04 Gildor Wang 阅读(3010) 评论(11) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述
2010-03-31 如何破解Windows Phone 7模拟器以运行所有内置应用