您的位置:首页 > 其它

我的WCF4 Rest Service及Entity Framework with POCO之旅(三)——用Entity Framework和POCO Template实现数据模型及存储

2013-12-16 16:37 483 查看


我的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…




选择一个连接,没有的话就创建一个:




然后对应的SQL脚本就自动生成好了:




打开WcfRestServiceDemo.edmx.sql,右键选择Execute SQL,成功后模型就同步到SQL Server了。





修改服务代码

现在数据模型和数据存储都做好了,我们要修改服务代码来使用新的数据模型。
删去Microblog.cs,添加对WcfRestServiceDemo.Data和System.Data.Entity的引用,并修改MicroblogService.cs代码如下:

+
View Code

要让Entity Framework正常工作,我们还需要把它生成的app.config文件中的ConnectionString复制到服务的web.config文件中:
<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(改了下次生成又会变回来):
[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,代码如下:

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代码如下:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐