<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_Gildor Wang</title><subtitle type="text">使用技术，但不放弃理解</subtitle><id>http://feed.cnblogs.com/blog/u/48211/rss</id><updated>2012-01-29T14:39:55Z</updated><author><name>Gildor Wang</name><uri>http://www.cnblogs.com/Gildor/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Gildor/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/48211/rss"/><entry><id>http://www.cnblogs.com/Gildor/archive/2011/08/22/2148804.html</id><title type="text">我的WCF4 REST Service及Entity Framework with POCO之旅（五）——身份验证</title><summary type="text">本文将通过对2-legged OAuth的简单实现，说明如何在WCF REST Service中实现自定义的身份验证。提到验证，最显而易见的做法当然是在每个服务方法开头调用一个检查权限的方法。但这种做...</summary><published>2011-08-21T17:15:00Z</published><updated>2011-08-21T17:15:00Z</updated><author><name>Gildor Wang</name><uri>http://www.cnblogs.com/Gildor/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Gildor/archive/2011/08/22/2148804.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Gildor/archive/2011/08/22/2148804.html"/><content type="html">&lt;p&gt;本文将通过对2-legged OAuth的简单实现，说明如何在WCF REST Service中实现自定义的身份验证。提到验证，最显而易见的做法当然是在每个服务方法开头调用一个检查权限的方法。但这种做法显然太不好看了，如果可以在每个方法上面用一个attribute标明哪些用户(组)可以访问，那就太好了。这就是本文要介绍的AuthorizationManager方案。&lt;/p&gt;  &lt;p&gt;首先简单提一下OAuth. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;OAuth&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;我们一般认为OAuth的用途就是给第三方应用/网站授权，比如我有twitter账号，我授权给yfrog读/写我的twitter，通过使用OAuth，在这个授权过程中，yfrog始终不会接触到我的密码，同时，如果我改了密码，它的访问也不会受到影响，如果我要停止向它授权，只需要在twitter设置中撤销访问权就行，安全，简单。然而这其实是OAuth的一个特定版本，叫做3-legged OAuth。OAuth还有一个版本叫做2-legged OAuth，是专门用于传统的单一客户端+服务器场景的。&lt;/p&gt;  &lt;p&gt;容易产生的疑问是，OAuth会不会太复杂？我只需要非常简单的身份验证而已，为什么要用这么麻烦的东西？其实OAuth, 尤其是2-legged Auth非常简单，而且如果你自己实现一个身份验证，当你把它实现完善时，你就会发现自己做了一个和OAuth一模一样的东西出来了，比如&lt;a href="http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/" target="_blank"&gt;这个哥们&lt;/a&gt;，他非常兴奋地声明自己发明了一种不用OAuth的身份验证，结果人家在&lt;a href="http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/comment-page-1/#comment-268609" target="_blank"&gt;评论&lt;/a&gt;里直接指出他写的就是2-legged OAuth。&lt;/p&gt;  &lt;p&gt;由于2-legged OAuth有一个初始条件是服务器和客户端已经获得了同样的Key/Secret对，也就是说身份验证的第一步是从服务器获得一个特定于用户的Secret Token。这个过程必须在安全的通道(如https)上完成，一般也就是一个简单的login就行了。本文为了简单起见，假定secret已经获得，而Key则是用户名。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;AuthorizationManger方案&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;要在服务方法调用之前验证身份，就得截获请求。之前看的很多实现方法都使用了WCF 3.5 REST WCF Starter Kit中的RequestInterceptor，但是在WCF 4.0中找不到了（只有在Data Services中还有QueryInterceptor)。所以必须找到另一个地方，在调用实际的服务方法之前完成身份验证。这样一个地方就是AuthorizationManager. 添加一个继承自ServiceAuthorizationManager的类：&lt;/p&gt;  public class AuthorizationManager : ServiceAuthorizationManager&lt;br/&gt;{&lt;br/&gt;    protected override bool CheckAccessCore(OperationContext operationContext)&lt;br/&gt;    {&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;这里的CheckAccessCore方法，就是我们要进行身份验证的地方，通过OperationContext参数，我们可以取到Request的URI以及Header。简单起见，先看OAuth的参数由Authorization header提供的情况。&lt;/p&gt;&lt;p&gt;以下是一个来自OAuth Spec的请求的例子，此处OAuth Consumer Key是dpf43f3p2l4k3l03，Consumer Secret是kd94hf93k423kf44。&lt;/p&gt;http://provider.example.net/profile&lt;br/&gt;&lt;br/&gt;Authorization: OAuth realm=&amp;quot;http://provider.example.net/&amp;quot;,&lt;br/&gt;oauth_consumer_key=&amp;quot;dpf43f3p2l4k3l03&amp;quot;,&lt;br/&gt;oauth_signature_method=&amp;quot;HMAC-SHA1&amp;quot;,&lt;br/&gt;oauth_signature=&amp;quot;IxyYZfG2BaKh8JyEGuHCOin%2F4bA%3D&amp;quot;,&lt;br/&gt;oauth_timestamp=&amp;quot;1191242096&amp;quot;,&lt;br/&gt;oauth_token=&amp;quot;&amp;quot;,&lt;br/&gt;oauth_nonce=&amp;quot;kllo9940pd9333jh&amp;quot;,&lt;br/&gt;oauth_version=&amp;quot;1.0&amp;quot;&lt;p&gt;服务端要做的，就是用OAuth的签名计算方法，把这个请求的签名计算出来，与上面的oauth_signature进行比较，如果符合，则身份验证通过。&lt;/p&gt;&lt;p&gt;首先我们将Authorization头解析为键值对。&lt;/p&gt;var authParamString = operationContext.GetHttpHeader(AuthHeaderName);&lt;br/&gt;if(authParamString == null || !authParamString.StartsWith(OAuthPrefix))&lt;br/&gt;{&lt;br/&gt;    SetAnonymousContext(operationContext.RequestContext.RequestMessage);&lt;br/&gt;    return true;&lt;br/&gt;}&lt;br/&gt;var authParams = authParamString.Substring(OAuthPrefix.Length).Split(',')&lt;br/&gt;    .Select(p =&amp;gt; p.Split('=')).ToDictionary(p =&amp;gt; p[0], p =&amp;gt; p[1].Trim('&amp;quot;'));&lt;p&gt;然后和method以及URI，当然还有密码的散列一起计算签名，并将计算得到的结果与客户端提交的进行比较：&lt;/p&gt;var uri = operationContext.RequestContext.RequestMessage.Headers.To.AbsoluteUri;&lt;br/&gt;var method = ((HttpRequestMessageProperty)operationContext.RequestContext&lt;br/&gt;    .RequestMessage.Properties[HttpRequestMessageProperty.Name]).Method;&lt;br/&gt;&lt;br/&gt;// Construct the base string.&lt;br/&gt;var baseString = JoinParameters(method, uri,&lt;br/&gt;    string.Format(&amp;quot;{0}={1}&amp;amp;{2}={3}&amp;amp;{4}={5}&amp;amp;{6}={7}&amp;amp;{8}={9}&amp;amp;{10}={11}&amp;quot;,&lt;br/&gt;    OAuthConsumerKey, authParams[OAuthConsumerKey],&lt;br/&gt;    OAuthNonce, authParams[OAuthNonce],&lt;br/&gt;    OAuthSignatureMethod, authParams[OAuthSignatureMethod],&lt;br/&gt;    OAuthTimestamp, authParams[OAuthTimestamp],&lt;br/&gt;    OAuthToken, authParams[OAuthToken],&lt;br/&gt;    OAuthVersion, authParams[OAuthVersion]));&lt;br/&gt;&lt;br/&gt;var signature = GetSignature(baseString, authParams[OAuthConsumerKey]);&lt;br/&gt;if(Uri.EscapeDataString(signature) != authParams[OAuthSignature])&lt;br/&gt;{&lt;br/&gt;    SetAnonymousContext(operationContext.RequestContext.RequestMessage);&lt;br/&gt;    return true;&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;// Authenticated successfully.&lt;br/&gt;SetSecurityContext(operationContext.RequestContext.RequestMessage, &lt;br/&gt;    authParams[OAuthConsumerKey]);&lt;br/&gt;return true;&lt;p&gt;简单起见，这里的GetSignature直接使用了HMAC-SHA1的签名算法，而没有按照客户端的指定：&lt;/p&gt;private string GetSignature(string baseString, string key)&lt;br/&gt;{&lt;br/&gt;    var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(GetAuthSecret(key) + &amp;quot;&amp;amp;&amp;quot;));&lt;br/&gt;    return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(baseString)));&lt;br/&gt;}&lt;p&gt;GetAuthScrect方法根据用户名(key)取出用户密码散列(secret)。值得注意的是，按照OAuth的标准，后面要加上一个”&amp;amp;”再参与计算。&lt;/p&gt;&lt;p&gt;SetSecurityContext方法设置当前的安全环境：&lt;/p&gt;private static void SetSecurityContext(Message request, string username)&lt;br/&gt;{&lt;br/&gt;    var policies = new List&amp;lt;IAuthorizationPolicy&amp;gt; &lt;br/&gt;    { new AuthorizationPolicy(new GenericIdentity(username)) };&lt;br/&gt;    var securityContext = new ServiceSecurityContext(policies.AsReadOnly());&lt;br/&gt;&lt;br/&gt;    if(request.Properties.Security != null)&lt;br/&gt;    {&lt;br/&gt;        request.Properties.Security.ServiceSecurityContext = securityContext;&lt;br/&gt;    }&lt;br/&gt;    else&lt;br/&gt;    {&lt;br/&gt;        request.Properties.Security = &lt;br/&gt;            new SecurityMessageProperty { ServiceSecurityContext = securityContext };&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;这里用到了一个名为AuthorizationPolicy的类，它非常简单，只是对IAuthorizationPolicy的基本实现而已。&lt;/p&gt;internal class AuthorizationPolicy : IAuthorizationPolicy&lt;br/&gt;{&lt;br/&gt;    private readonly Guid _id = Guid.NewGuid();&lt;br/&gt;&lt;br/&gt;    private readonly IIdentity _identity;&lt;br/&gt;&lt;br/&gt;    public AuthorizationPolicy(IIdentity identity)&lt;br/&gt;    {&lt;br/&gt;        if(identity == null)&lt;br/&gt;        {&lt;br/&gt;            throw new ArgumentNullException(&amp;quot;identity&amp;quot;);&lt;br/&gt;        }&lt;br/&gt;        _identity = identity;&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #region IAuthorizationPolicy Members&lt;br/&gt;&lt;br/&gt;    // this method gets called after the authentication stage&lt;br/&gt;    /// &amp;lt;summary&amp;gt;&lt;br/&gt;    /// Evaluates whether a user meets the requirements for this authorization policy.&lt;br/&gt;    /// &amp;lt;/summary&amp;gt;&lt;br/&gt;    /// &amp;lt;param name=&amp;quot;evaluationContext&amp;quot;&amp;gt;An &amp;lt;see cref=&amp;quot;T:System.IdentityModel.Policy.EvaluationContext&amp;quot;/&amp;gt; that contains the claim set that the authorization policy evaluates.&amp;lt;/param&amp;gt;&lt;br/&gt;    /// &amp;lt;param name=&amp;quot;state&amp;quot;&amp;gt;A &amp;lt;see cref=&amp;quot;T:System.Object&amp;quot;/&amp;gt;, passed by reference that represents the custom state for this authorization policy.&amp;lt;/param&amp;gt;&lt;br/&gt;    /// &amp;lt;returns&amp;gt;&lt;br/&gt;    /// false if the &amp;lt;see cref=&amp;quot;M:System.IdentityModel.Policy.IAuthorizationPolicy.Evaluate(System.IdentityModel.Policy.EvaluationContext,System.Object@)&amp;quot;/&amp;gt; method for this authorization policy must be called if additional claims are added by other authorization policies to &amp;lt;paramref name=&amp;quot;evaluationContext&amp;quot;/&amp;gt;; otherwise, true to state no additional evaluation is required by this authorization policy.&lt;br/&gt;    /// &amp;lt;/returns&amp;gt;&lt;br/&gt;    public bool Evaluate(EvaluationContext evaluationContext, ref object state)&lt;br/&gt;    {&lt;br/&gt;        // get the authenticated client identity&lt;br/&gt;        var client = _identity; //GetClientIdentity(evaluationContext);&lt;br/&gt;        // set the custom principal&lt;br/&gt;        evaluationContext.Properties[&amp;quot;Principal&amp;quot;] = new CustomPrincipal(client);&lt;br/&gt;        HttpContext.Current.User = new CustomPrincipal(_identity);&lt;br/&gt;        return true;&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public ClaimSet Issuer&lt;br/&gt;    {&lt;br/&gt;        get { return ClaimSet.System; }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public string Id&lt;br/&gt;    {&lt;br/&gt;        get { return _id.ToString(); }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #endregion&lt;br/&gt;}&lt;p&gt;而CustomPrincipal则是IPrincipal的基本实现。&lt;/p&gt;internal class CustomPrincipal : IPrincipal&lt;br/&gt;{&lt;br/&gt;    private readonly IIdentity _identity;&lt;br/&gt;&lt;br/&gt;    public CustomPrincipal(IIdentity identity)&lt;br/&gt;    {&lt;br/&gt;        _identity = identity;&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    /// &amp;lt;summary&amp;gt;&lt;br/&gt;    /// Gets the &amp;lt;see cref=&amp;quot;CustomPrincipal&amp;quot;/&amp;gt; instance for the current user.&lt;br/&gt;    /// It's a helper method for easy access (without casting).&lt;br/&gt;    /// &amp;lt;/summary&amp;gt;&lt;br/&gt;    public static CustomPrincipal Current&lt;br/&gt;    {&lt;br/&gt;        get { return Thread.CurrentPrincipal as CustomPrincipal; }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public string[] Roles&lt;br/&gt;    {&lt;br/&gt;        get { return System.Web.Security.Roles.GetRolesForUser(_identity.Name); }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #region IPrincipal Members&lt;br/&gt;&lt;br/&gt;    public IIdentity Identity&lt;br/&gt;    {&lt;br/&gt;        get { return _identity; }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public bool IsInRole(string role)&lt;br/&gt;    {&lt;br/&gt;        return Roles.Contains(role);&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    #endregion&lt;br/&gt;}&lt;p&gt;&lt;strong&gt;修改web.config&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;要让AuthorizationManager嵌入WCF Service工作，还需最后一步，修改web.config，在&amp;lt;system.serviceModel&amp;gt;节中添加如下内容：&lt;/p&gt;&amp;lt;behaviors&amp;gt;&lt;br/&gt;  &amp;lt;serviceBehaviors&amp;gt;&lt;br/&gt;    &amp;lt;behavior name=&amp;quot;CustomServiceBehavior&amp;quot;&amp;gt;&lt;br/&gt;      &amp;lt;serviceAuthorization principalPermissionMode=&amp;quot;Custom&amp;quot; serviceAuthorizationManagerType=&amp;quot;WcfRestServiceDemo.Service.AuthorizationManager, WcfRestServiceDemo.Service&amp;quot;&amp;gt;&lt;br/&gt;      &amp;lt;/serviceAuthorization&amp;gt;&lt;br/&gt;      &amp;lt;serviceDebug includeExceptionDetailInFaults=&amp;quot;true&amp;quot;/&amp;gt;&lt;br/&gt;    &amp;lt;/behavior&amp;gt;&lt;br/&gt;  &amp;lt;/serviceBehaviors&amp;gt;&lt;br/&gt;&amp;lt;/behaviors&amp;gt;&lt;br/&gt;&amp;lt;services&amp;gt;&lt;br/&gt;  &amp;lt;service name=&amp;quot;WcfRestServiceDemo.Service.MicroblogService&amp;quot; behaviorConfiguration=&amp;quot;CustomServiceBehavior&amp;quot;&amp;gt;&lt;br/&gt;  &amp;lt;/service&amp;gt;&lt;br/&gt;&amp;lt;/services&amp;gt;&lt;p&gt;这里我们定义了一个serviceBehavior来使用自定义的AuthorizationManager，然后指定MicroblogService使用这个behavior. 此处&amp;lt;serviceDebug includeExceptionDetailInFaults=&amp;quot;true&amp;quot;/&amp;gt;是可选的，只是为了在出错时可以看到详细的错误信息。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;定义访问策略&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;好了，框架都已经搭好了，现在就可以开始定义访问策略了。方法非常简单，只需要在服务方法上面加上PrincipalPermission attribute就可以了。策略的对象可以是User，也可以是Role。&lt;/p&gt;&lt;p&gt;比如，要限制仅允许开头示例请求中的用户”dpf43f3p2l4k3l03”获取微博列表，则可以修改GetCollection方法：&lt;/p&gt;[WebGet(UriTemplate = &amp;quot;&amp;quot;)]&lt;br/&gt;[PrincipalPermission(SecurityAction.Demand, Name = &amp;quot;dpf43f3p2l4k3l03&amp;quot;)]&lt;br/&gt;public List&amp;lt;Microblog&amp;gt; GetCollection()&lt;br/&gt;{&lt;br/&gt;    ...&lt;p&gt;改好之后，直接运行一下试试：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201108/201108220112196103.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201108/201108220114423630.png" width="644" height="283" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;如果把includeExceptionDetailInFaults关掉，则显示：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201108/201108220114464596.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201108/201108220114528429.png" width="644" height="283" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;用Fiddler2可以看到这时返回的是一个400 Bad Request。这个效果并不好，因为这时应该返回401 Unauthorized，以后会解决这个问题。至少阻止匿名用户访问的目的是达到了。&lt;/p&gt;&lt;p&gt;接下来，在Fiddler2中制造一个请求，提供完整的Authorization头。由于Host URI变了，所以签名也和开头示例中的有所不同：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201108/201108220114567476.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201108/201108220115123251.png" width="619" height="484" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;可以看到，请求通过了身份验证，成功获取了数据。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;小结&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;本文介绍了：&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;如何使用AuthorizationManager来截获请求并完成基于OAuth的身份验证 &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;不足之处&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;容易看出，本文的实现还很不完善：&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;设置访问策略时，尚不能使用Role。Role其实是由CustomPrincipal的IsInRole方法来提供的，现在的实现是直接调用System.Web.Security.Roles，那就需要配置RoleProvider。比较简单的做法是直接使用ASP.NET Membership那一整套东西。当然也可以直接在CustomPrincipal中实现自定义的Role逻辑。 &lt;/li&gt;  &lt;li&gt;简单起见，我对OAuth的实现很基本，存在很多问题     &lt;ul&gt;      &lt;li&gt;不支持在URI或者Form body中提供OAuth参数 &lt;/li&gt;      &lt;li&gt;不支持HMAC-SHA1以外的签名算法 &lt;/li&gt;      &lt;li&gt;没有检查timestamp参数和服务器时间的差距 &lt;/li&gt;      &lt;li&gt;没有检查nonce参数的唯一性 &lt;/li&gt;      &lt;li&gt;没有检查realm参数（可选的） &lt;/li&gt;    &lt;/ul&gt;  &lt;/li&gt;  &lt;li&gt;此外，身份验证失败的返回信息并不规范，这个将在后面解决。 &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;毕竟，我只是想要演示如何在WCF目前的框架范围内实现这一过程，实际生产应用的话，还是推荐使用成熟的library.&lt;/p&gt;&lt;p&gt;我将在下一篇中完善Microblog服务的身份验证功能，为其添加一个简单的用户模块。&lt;/p&gt;&lt;p&gt;&lt;a href="http://files.cnblogs.com/Gildor/WcfRestServiceDemo.ch5.zip"&gt;&lt;em&gt;本文代码&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;&lt;em&gt;参考：&lt;/em&gt;&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;&lt;a href="http://oauth.googlecode.com/svn/spec/ext/consumer_request/1.0/drafts/2/spec.html" target="_blank"&gt;&lt;em&gt;Using OAuth for Consumer Requests&lt;/em&gt;&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://cakebaker.42dh.com/2011/01/10/2-legged-vs-3-legged-oauth/" target="_blank"&gt;&lt;em&gt;2-legged vs. 3-legged OAuth&lt;/em&gt;&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/ms731774.aspx" target="_blank"&gt;&lt;em&gt;How to: Create a Custom Authorization Manager for a Service&lt;/em&gt;&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cleancode.co.nz/blog/523/oauth-dot-net" target="_blank"&gt;&lt;em&gt;OAuth and .Net&lt;/em&gt;&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://tools.ietf.org/html/rfc5849" target="_blank"&gt;&lt;em&gt;RFC 5849 - The OAuth 1.0 Protocol&lt;/em&gt;&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/" target="_blank"&gt;&lt;em&gt;Designing a Secure REST (Web) API without OAuth&lt;/em&gt;&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://social.msdn.microsoft.com/Forums/en-GB/wcfprerelease/thread/cb4f6b1f-519f-49bf-84ac-7f5fdf411af7" target="_blank"&gt;&lt;em&gt;WCF 4.0 REST Authorization Examples&lt;/em&gt;&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt;&lt;hr /&gt;&lt;p&gt;&lt;strong&gt;我的WCF4 REST Service及Entity Framework with POCO之旅系列&lt;/strong&gt; &lt;/p&gt;&lt;ul&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/28/1997426.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（一）——创建一个基本的RESTful Service&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/29/1998392.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（二）——选择请求/返回格式&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/31/2000553.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（三）——用Entity Framework和POCO Template实现数据模型及存储&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/04/20/2022013.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（四）——定制Entity&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/08/22/2148804.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（五）——身份验证&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt;&lt;img src="http://www.cnblogs.com/Gildor/aggbug/2148804.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/08/22/2148804.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Gildor/archive/2011/08/10/2134082.html</id><title type="text">我的WCF4 Rest Service及Entity Framework with POCO之旅&amp;mdash;&amp;mdash;写在中间</title><summary type="text">距离我写这个系列的上一篇文章已经过去了3个多月，这期间我自己发生了很多变化，技术圈甚至单单是WCF领域更是往前走了一大步。上个月我听了一个关于WCF Web API的talk，发现我之前用各种方法拐着...</summary><published>2011-08-10T11:54:00Z</published><updated>2011-08-10T11:54:00Z</updated><author><name>Gildor Wang</name><uri>http://www.cnblogs.com/Gildor/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Gildor/archive/2011/08/10/2134082.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Gildor/archive/2011/08/10/2134082.html"/><content type="html">&lt;p&gt;距离我写这个系列的上一篇文章已经过去了3个多月，这期间我自己发生了很多变化，技术圈甚至单单是WCF领域更是往前走了一大步。上个月我听了一个关于WCF Web API的talk，发现我之前用各种方法拐着弯才能做到的事情，他们都原生实现了，当然许多解决方案要比我所探索的更加优美、易用。事实上自从前一篇文章发布以来，我都没有再写关于WCF的代码，也没有去关注它的发展情况，因此听到关于WCF Web API的介绍的时候我多少有一点惊讶，感觉自己似乎像落后了几年一样——事实上这不过是短短的几个月时间而已。当然，另一方面，由于他们的许多成果中包括了我之前做过的事情，我也感到非常高兴，因为这证明了我自己的探索思路是正确的，符合大多数开发者的需求。至于WCF新的框架到底有多大改变，它现在已经发布到&lt;a href="http://wcf.codeplex.com/releases/view/64449" target="_blank"&gt;Preview 4&lt;/a&gt;了，不妨拿下来玩一下。我自己也还没有试过，不知道他们在talk中提及的那些特性，是否都已经实现了。&lt;/p&gt;  &lt;p&gt;因此，对于我之前打过的一些草稿，我感到很犹豫，如果新的框架发布，那么我所介绍的很多东西就没有意义了。而且很多问题，园子里的高人们也讨论得很细致了。然而新框架毕竟还未发布，发布后也未必所有人都能够在实际环境中用上，而谈技术的文章是不嫌多的。所以还是把原定计划中的剩下几篇文章发上来，争取每周一篇，希望能够对用得上的人有所帮助，也对自己有个交代。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Agenda:&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;我的WCF4 Rest Service及Entity Framework with POCO之旅（五）——身份验证&amp;amp;OAuth&lt;/li&gt;    &lt;li&gt;我的WCF4 Rest Service及Entity Framework with POCO之旅（六）——错误处理&lt;/li&gt;    &lt;li&gt;我的WCF4 Rest Service及Entity Framework with POCO之旅（七）——参数检查&lt;/li&gt;    &lt;li&gt;我的WCF4 Rest Service及Entity Framework with POCO之旅（八）——自定义QueryStringConverter&lt;/li&gt;    &lt;li&gt;我的WCF4 Rest Service及Entity Framework with POCO之旅（九）——按需返回部分Entity字段&lt;/li&gt;    &lt;li&gt;我的WCF4 Rest Service及Entity Framework with POCO之旅（十）——测试？&lt;/li&gt;    &lt;li&gt;我的WCF4 Rest Service及Entity Framework with POCO之旅（十一）——其他几个小问题&lt;/li&gt; &lt;/ul&gt;&lt;img src="http://www.cnblogs.com/Gildor/aggbug/2134082.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/08/10/2134082.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Gildor/archive/2011/08/10/2134010.html</id><title type="text">导出/导入Visual Studio 2010扩展</title><summary type="text">Visual Studio 2010的extensions不会像Android上的app那样，通过“云推送”安装到你的每台计算机上，甚至也没有像configuration那样提供一个导出/导入的功能。...</summary><published>2011-08-10T11:09:00Z</published><updated>2011-08-10T11:09:00Z</updated><author><name>Gildor Wang</name><uri>http://www.cnblogs.com/Gildor/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Gildor/archive/2011/08/10/2134010.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Gildor/archive/2011/08/10/2134010.html"/><content type="html">&lt;p&gt;Visual Studio 2010的extensions不会像Android上的app那样，通过“云推送”安装到你的每台计算机上，甚至也没有像configuration那样提供一个导出/导入的功能。每次部署开发环境，重新安装扩展是一件费时费神的工作。事实上，绝大多数在Extension Manager中安装的扩展都在&lt;/p&gt;  %localappdata%\microsoft\VisualStudio\10.0\Extensions下面。只要将这个文件夹中的内容备份/还原，就可以在Visual Studio的Extension Manager中看到原来的扩展了，而且Visual Studio也会检查这些扩展的更新情况并且提示更新，就好像这些扩展一开始就装在那里一样。只不过，当这些扩展刚还原时，VS默认会Disable它们，必须手动逐个Enable才行。这我还没有发现比较好的解决办法，应该有个地方设置了每个扩展的启用情况，如果有哪位知道不妨告知。&lt;img src="http://www.cnblogs.com/Gildor/aggbug/2134010.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/08/10/2134010.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Gildor/archive/2011/04/20/2022013.html</id><title type="text">我的WCF4 Rest Service及Entity Framework with POCO之旅（四）——定制Entity</title><summary type="text">本文将focus几个结合使用WCF REST和Entity Framework with POCO的常见问题。</summary><published>2011-04-20T02:44:00Z</published><updated>2011-04-20T02:44:00Z</updated><author><name>Gildor Wang</name><uri>http://www.cnblogs.com/Gildor/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Gildor/archive/2011/04/20/2022013.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Gildor/archive/2011/04/20/2022013.html"/><content type="html">&lt;p&gt;本文将focus几个结合使用WCF REST和Entity Framework with POCO的常见问题。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Entity Type和Property名称的大小写&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;按照RESTful的习惯，XML或者JSON格式的数据的node名称开头字母一般使用小写，比如，下面是一段Google Buzz API的RESTful返回信息：&lt;/p&gt;  &amp;lt;entry xmlns=&amp;quot;http://www.w3.org/2005/Atom&amp;quot; xmlns:activity=&amp;quot;http://activitystrea.ms/spec/1.0&amp;quot;&amp;gt;&lt;br/&gt;  &amp;lt;id&amp;gt;tag:google.com,2010:buzz:z12puk22ajfyzsz&amp;lt;/id&amp;gt;&lt;br/&gt;  &amp;lt;author&amp;gt;&lt;br/&gt;    &amp;lt;name&amp;gt;Ted Taco&amp;lt;/name&amp;gt;&lt;br/&gt;    &amp;lt;uri&amp;gt;http://www.google.com/profiles/ted&amp;lt;/uri&amp;gt;&lt;br/&gt;  &amp;lt;/author&amp;gt;&lt;br/&gt;  &amp;lt;published&amp;gt;2010-04-23T11:03:34.342Z&amp;lt;/published&amp;gt;&lt;br/&gt;  &amp;lt;updated&amp;gt;2010-04-23T11:03:34.342Z&amp;lt;/updated&amp;gt;&lt;br/&gt;  &amp;lt;activity:object&amp;gt;&lt;br/&gt;    &amp;lt;activity:object-type&amp;gt;http://activitystrea.ms/schema/1.0/note&amp;lt;/activity:object-type&amp;gt;&lt;br/&gt;    &amp;lt;content type=&amp;quot;html&amp;quot;&amp;gt;Hey, this is my first Buzz Post!&amp;lt;/content&amp;gt;&lt;br/&gt;  &amp;lt;activity:object&amp;gt;&lt;br/&gt;  &amp;lt;link rel=&amp;quot;alternate&amp;quot; type=&amp;quot;text/html&amp;quot; href=&amp;quot;http://www.google.com/buzz/ted/Hey-this-is-my-first-Buzz-Post&amp;quot; /&amp;gt;&lt;br/&gt;  &amp;lt;!-- more data --&amp;gt;&lt;br/&gt;&amp;lt;/entry&amp;gt;&lt;p&gt;要让我们的Service返回数据node也使用小写字母开头，我们需要给Entity加上[DataContract]和[DataMember] attributes。（当然也可以修改WCF的behavior让它使用我们提供的的serializer但是现在框架既然还能满足需求，我还不想那么做。）   &lt;br /&gt;手动加attribute显然不是一个好主意，而且可以想见一旦模型代码重新生成，我们的修改就都没了。我们需要修改生成Entity的T4模板(Model.tt和Model.Context.tt)。   &lt;br /&gt;T4模板和其他的一些代码生成器的模板很相似，和现在的ASP.NET也有点相似。主要特点就是在模板内容（对于模板来说类似于纯文本）中嵌入C#代码。   &lt;br /&gt;由于T4模板的可读性不是很好，而VS原生又不支持T4的高亮和intellisense，开始编辑之前，最好装一个支持T4高亮的插件。我用的是Visual T4，虽然反应比较慢还有一些bug，不过是免费的，基本也够用了。&lt;/p&gt;&lt;p&gt;我们先看一下现在的Model.tt的全貌：&lt;/p&gt;&amp;lt;#@ template language=&amp;quot;C#&amp;quot; debug=&amp;quot;false&amp;quot; hostspecific=&amp;quot;true&amp;quot;#&amp;gt;&lt;br/&gt;&amp;lt;#@ include file=&amp;quot;EF.Utility.CS.ttinclude&amp;quot;#&amp;gt;&amp;lt;#@&lt;br/&gt; output extension=&amp;quot;.cs&amp;quot;#&amp;gt;&amp;lt;#&lt;br/&gt;&lt;br/&gt;CodeGenerationTools code = new CodeGenerationTools(this);&lt;br/&gt;MetadataLoader loader = new MetadataLoader(this);&lt;br/&gt;CodeRegion region = new CodeRegion(this, 1);&lt;br/&gt;MetadataTools ef = new MetadataTools(this);&lt;br/&gt;&lt;br/&gt;string inputFile = @&amp;quot;WcfRestServiceDemo.edmx&amp;quot;;&lt;br/&gt;EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);&lt;br/&gt;string namespaceName = code.VsNamespaceSuggestion();&lt;br/&gt;&lt;br/&gt;EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);&lt;br/&gt;&lt;br/&gt;// Write out support code to primary template output file&lt;br/&gt;WriteHeader(fileManager);&lt;br/&gt;BeginNamespace(namespaceName, code);&lt;br/&gt;WriteCustomObservableCollection();&lt;br/&gt;EndNamespace(namespaceName);&lt;br/&gt;&lt;br/&gt;// Emit Entity Types&lt;br/&gt;foreach (EntityType entity in ItemCollection.GetItems&amp;lt;EntityType&amp;gt;().OrderBy(e =&amp;gt; e.Name))&lt;br/&gt;{&lt;br/&gt;    fileManager.StartNewFile(entity.Name + &amp;quot;.cs&amp;quot;);&lt;br/&gt;    BeginNamespace(namespaceName, code);&lt;br/&gt;    bool entityHasNullableFKs = entity.NavigationProperties.Any(np =&amp;gt; np.GetDependentProperties().Any(p=&amp;gt;ef.IsNullable(p)));&lt;br/&gt;#&amp;gt;&lt;br/&gt;&amp;lt;#=Accessibility.ForType(entity)#&amp;gt; &amp;lt;#=code.SpaceAfter(code.AbstractOption(entity))#&amp;gt;partial class &amp;lt;#=code.Escape(entity)#&amp;gt;&amp;lt;#=code.StringBefore(&amp;quot; : &amp;quot;, code.Escape(entity.BaseType))#&amp;gt;&lt;br/&gt;{&lt;br/&gt;&amp;lt;#&lt;br/&gt;    region.Begin(&amp;quot;Primitive Properties&amp;quot;);&lt;br/&gt;&lt;br/&gt;    foreach (EdmProperty edmProperty in entity.Properties.Where(p =&amp;gt; p.TypeUsage.EdmType is PrimitiveType &amp;amp;&amp;amp; p.DeclaringType == entity))&lt;br/&gt;    {&lt;br/&gt;        bool isForeignKey = entity.NavigationProperties.Any(np=&amp;gt;np.GetDependentProperties().Contains(edmProperty));&lt;br/&gt;        bool isDefaultValueDefinedInModel = (edmProperty.DefaultValue != null);&lt;br/&gt;        bool generateAutomaticProperty = false;&lt;br/&gt;&lt;br/&gt;#&amp;gt;&lt;br/&gt;&lt;br/&gt;    &amp;lt;#=PropertyVirtualModifier(Accessibility.ForProperty(edmProperty))#&amp;gt; &amp;lt;#=code.Escape(edmProperty.TypeUsage)#&amp;gt; &amp;lt;#=code.Escape(edmProperty)#&amp;gt;&lt;br/&gt;    {&lt;br/&gt;&amp;lt;#&lt;br/&gt;        if (isForeignKey)&lt;br/&gt;        {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#&amp;gt;get { return &amp;lt;#=code.FieldName(edmProperty)#&amp;gt;; }&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#&amp;gt;set&lt;br/&gt;        {&lt;br/&gt;&amp;lt;#&lt;br/&gt;            if (entityHasNullableFKs)&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            try&lt;br/&gt;            {&lt;br/&gt;                _settingFK = true;&lt;br/&gt;&amp;lt;#&lt;br/&gt;                PushIndent(CodeRegion.GetIndent(1));&lt;br/&gt;            }&lt;br/&gt;            if (((PrimitiveType)edmProperty.TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.Binary)&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            if (!StructuralComparisons.StructuralEqualityComparer.Equals(&amp;lt;#=code.FieldName(edmProperty)#&amp;gt;, value))&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;            else&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            if (&amp;lt;#=code.FieldName(edmProperty)#&amp;gt; != value)&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;#&amp;gt;&lt;br/&gt;            {&lt;br/&gt;&amp;lt;#&lt;br/&gt;            foreach (var np in entity.NavigationProperties.Where(np=&amp;gt;np.GetDependentProperties().Contains(edmProperty)))&lt;br/&gt;            {&lt;br/&gt;                EdmProperty principalProperty = ef.GetCorrespondingPrincipalProperty(np, edmProperty);&lt;br/&gt;                if (((PrimitiveType)principalProperty.TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.Binary)&lt;br/&gt;                {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                if ((&amp;lt;#=code.Escape(np)#&amp;gt; != null) &amp;amp;&amp;amp; !StructuralComparisons.StructuralEqualityComparer.Equals(&amp;lt;#=code.Escape(np)#&amp;gt;.&amp;lt;#=code.Escape(principalProperty)#&amp;gt;, value))&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;                else&lt;br/&gt;                {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                if (&amp;lt;#=code.Escape(np)#&amp;gt; != null &amp;amp;&amp;amp; &amp;lt;#=code.Escape(np)#&amp;gt;.&amp;lt;#=code.Escape(principalProperty)#&amp;gt; != value)&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;#&amp;gt;&lt;br/&gt;                {&lt;br/&gt;&amp;lt;#&lt;br/&gt;                if (!(np.GetDependentProperties().Where(p=&amp;gt;ef.IsNullable(p)).Any() &amp;amp;&amp;amp;&lt;br/&gt;                      np.GetDependentProperties().Count() &amp;gt; 1))&lt;br/&gt;                {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                    &amp;lt;#=code.Escape(np)#&amp;gt; = null;&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;                else&lt;br/&gt;                {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                    var previousValue = &amp;lt;#=code.FieldName(np)#&amp;gt;;&lt;br/&gt;                    &amp;lt;#=code.FieldName(np)#&amp;gt; = null;&lt;br/&gt;                    Fixup&amp;lt;#=np.Name#&amp;gt;(previousValue, skipKeys: true);&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;#&amp;gt;&lt;br/&gt;                }&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;#&amp;gt;&lt;br/&gt;                &amp;lt;#=code.FieldName(edmProperty)#&amp;gt; = value;&lt;br/&gt;            }&lt;br/&gt;&amp;lt;#&lt;br/&gt;            if (entityHasNullableFKs)&lt;br/&gt;            {&lt;br/&gt;                PopIndent();&lt;br/&gt;#&amp;gt;&lt;br/&gt;            }&lt;br/&gt;            finally&lt;br/&gt;            {&lt;br/&gt;                _settingFK = false;&lt;br/&gt;            }&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;#&amp;gt;&lt;br/&gt;        }&lt;br/&gt;&amp;lt;#&lt;br/&gt;        }&lt;br/&gt;        else if (isDefaultValueDefinedInModel)&lt;br/&gt;        {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#&amp;gt;get { return &amp;lt;#=code.FieldName(edmProperty)#&amp;gt;; }&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#&amp;gt;set { &amp;lt;#=code.FieldName(edmProperty)#&amp;gt; = value; }&lt;br/&gt;&amp;lt;#&lt;br/&gt;        }&lt;br/&gt;        else&lt;br/&gt;        {&lt;br/&gt;            generateAutomaticProperty = true;&lt;br/&gt;#&amp;gt;&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#&amp;gt;get;&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#&amp;gt;set;&lt;br/&gt;&amp;lt;#&lt;br/&gt;        }&lt;br/&gt;#&amp;gt;&lt;br/&gt;    }&lt;br/&gt;&amp;lt;#&lt;br/&gt;        if (!generateAutomaticProperty)&lt;br/&gt;        {&lt;br/&gt;#&amp;gt;&lt;br/&gt;    private &amp;lt;#=code.Escape(edmProperty.TypeUsage)#&amp;gt; &amp;lt;#=code.FieldName(edmProperty)#&amp;gt;&amp;lt;#=code.StringBefore(&amp;quot; = &amp;quot;, code.CreateLiteral(edmProperty.DefaultValue))#&amp;gt;;&lt;br/&gt;&amp;lt;#&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    region.End();&lt;br/&gt;&lt;br/&gt;    region.Begin(&amp;quot;Complex Properties&amp;quot;);&lt;br/&gt;&lt;br/&gt;    foreach(EdmProperty edmProperty in entity.Properties.Where(p =&amp;gt; p.TypeUsage.EdmType is ComplexType &amp;amp;&amp;amp; p.DeclaringType == entity))&lt;br/&gt;    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;&lt;br/&gt;    &amp;lt;#=PropertyVirtualModifier(Accessibility.ForProperty(edmProperty))#&amp;gt; &amp;lt;#=code.Escape(edmProperty.TypeUsage)#&amp;gt; &amp;lt;#=code.Escape(edmProperty)#&amp;gt;&lt;br/&gt;    {&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#&amp;gt;get { return &amp;lt;#=code.FieldName(edmProperty)#&amp;gt;; }&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#&amp;gt;set { &amp;lt;#=code.FieldName(edmProperty)#&amp;gt; = value; }&lt;br/&gt;    }&lt;br/&gt;    private &amp;lt;#=code.Escape(edmProperty.TypeUsage)#&amp;gt; &amp;lt;#=code.FieldName(edmProperty)#&amp;gt; = new &amp;lt;#=code.Escape(edmProperty.TypeUsage)#&amp;gt;();&lt;br/&gt;&amp;lt;#&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    region.End();&lt;br/&gt;&lt;br/&gt;    ////////&lt;br/&gt;    //////// Write Navigation properties -------------------------------------------------------------------------------------------&lt;br/&gt;    ////////&lt;br/&gt;&lt;br/&gt;    region.Begin(&amp;quot;Navigation Properties&amp;quot;);&lt;br/&gt;&lt;br/&gt;    foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np =&amp;gt; np.DeclaringType == entity))&lt;br/&gt;    {&lt;br/&gt;        NavigationProperty inverse = ef.Inverse(navProperty);&lt;br/&gt;        if (inverse != null &amp;amp;&amp;amp;  !IsReadWriteAccessibleProperty(inverse))&lt;br/&gt;        {&lt;br/&gt;            inverse = null;&lt;br/&gt;        }&lt;br/&gt;#&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;#&lt;br/&gt;        if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)&lt;br/&gt;        {&lt;br/&gt;#&amp;gt;&lt;br/&gt;    &amp;lt;#=PropertyVirtualModifier(Accessibility.ForReadOnlyProperty(navProperty))#&amp;gt; ICollection&amp;lt;&amp;lt;#=code.Escape(navProperty.ToEndMember.GetEntityType())#&amp;gt;&amp;gt; &amp;lt;#=code.Escape(navProperty)#&amp;gt;&lt;br/&gt;    {&lt;br/&gt;        get&lt;br/&gt;        {&lt;br/&gt;            if (&amp;lt;#=code.FieldName(navProperty)#&amp;gt; == null)&lt;br/&gt;            {&lt;br/&gt;&amp;lt;#&lt;br/&gt;                if (inverse != null || ((AssociationType)navProperty.RelationshipType).IsForeignKey)&lt;br/&gt;                {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                var newCollection = new FixupCollection&amp;lt;&amp;lt;#=code.Escape(navProperty.ToEndMember.GetEntityType())#&amp;gt;&amp;gt;();&lt;br/&gt;                newCollection.CollectionChanged += Fixup&amp;lt;#=navProperty.Name#&amp;gt;;&lt;br/&gt;                &amp;lt;#=code.FieldName(navProperty)#&amp;gt; = newCollection;&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;                else&lt;br/&gt;                {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                &amp;lt;#=code.FieldName(navProperty)#&amp;gt; = new FixupCollection&amp;lt;&amp;lt;#=code.Escape(navProperty.ToEndMember.GetEntityType())#&amp;gt;&amp;gt;();&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;#&amp;gt;&lt;br/&gt;            }&lt;br/&gt;            return &amp;lt;#=code.FieldName(navProperty)#&amp;gt;;&lt;br/&gt;        }&lt;br/&gt;        set&lt;br/&gt;        {&lt;br/&gt;&amp;lt;#&lt;br/&gt;            if (inverse != null || ((AssociationType)navProperty.RelationshipType).IsForeignKey)&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            if (!ReferenceEquals(&amp;lt;#=code.FieldName(navProperty)#&amp;gt;, value))&lt;br/&gt;            {&lt;br/&gt;                var previousValue = &amp;lt;#=code.FieldName(navProperty)#&amp;gt; as FixupCollection&amp;lt;&amp;lt;#=code.Escape(navProperty.ToEndMember.GetEntityType())#&amp;gt;&amp;gt;;&lt;br/&gt;                if (previousValue != null)&lt;br/&gt;                {&lt;br/&gt;                    previousValue.CollectionChanged -= Fixup&amp;lt;#=navProperty.Name#&amp;gt;;&lt;br/&gt;                }&lt;br/&gt;                &amp;lt;#=code.FieldName(navProperty)#&amp;gt; = value;&lt;br/&gt;                var newValue = value as FixupCollection&amp;lt;&amp;lt;#=code.Escape(navProperty.ToEndMember.GetEntityType())#&amp;gt;&amp;gt;;&lt;br/&gt;                if (newValue != null)&lt;br/&gt;                {&lt;br/&gt;                    newValue.CollectionChanged += Fixup&amp;lt;#=navProperty.Name#&amp;gt;;&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;            else&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            &amp;lt;#=code.FieldName(navProperty)#&amp;gt; = value;&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;#&amp;gt;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    private ICollection&amp;lt;&amp;lt;#=code.Escape(navProperty.ToEndMember.GetEntityType())#&amp;gt;&amp;gt; &amp;lt;#=code.FieldName(navProperty)#&amp;gt;;&lt;br/&gt;&amp;lt;#&lt;br/&gt;        }&lt;br/&gt;        else&lt;br/&gt;        {&lt;br/&gt;#&amp;gt;&lt;br/&gt;    &amp;lt;#=PropertyVirtualModifier(Accessibility.ForProperty(navProperty))#&amp;gt; &amp;lt;#=code.Escape(navProperty.ToEndMember.GetEntityType())#&amp;gt; &amp;lt;#=code.Escape(navProperty)#&amp;gt;&lt;br/&gt;    {&lt;br/&gt;&amp;lt;#&lt;br/&gt;            if (inverse != null || ((AssociationType)navProperty.RelationshipType).IsForeignKey)&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForGetter(navProperty))#&amp;gt;get { return &amp;lt;#=code.FieldName(navProperty)#&amp;gt;; }&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForSetter(navProperty))#&amp;gt;set&lt;br/&gt;        {&lt;br/&gt;            if (!ReferenceEquals(&amp;lt;#=code.FieldName(navProperty)#&amp;gt;, value))&lt;br/&gt;            {&lt;br/&gt;                var previousValue = &amp;lt;#=code.FieldName(navProperty)#&amp;gt;;&lt;br/&gt;                &amp;lt;#=code.FieldName(navProperty)#&amp;gt; = value;&lt;br/&gt;                Fixup&amp;lt;#=navProperty.Name#&amp;gt;(previousValue);&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    private &amp;lt;#=code.Escape(navProperty.ToEndMember.GetEntityType())#&amp;gt; &amp;lt;#=code.FieldName(navProperty)#&amp;gt;;&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;            else&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForGetter(navProperty))#&amp;gt;get;&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForSetter(navProperty))#&amp;gt;set;&lt;br/&gt;    }&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;    region.End();&lt;br/&gt;&lt;br/&gt;    region.Begin(&amp;quot;Association Fixup&amp;quot;);&lt;br/&gt;&lt;br/&gt;    if (entityHasNullableFKs)&lt;br/&gt;    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;&lt;br/&gt;    private bool _settingFK = false;&lt;br/&gt;&amp;lt;#&lt;br/&gt;    }&lt;br/&gt;    foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np =&amp;gt; np.DeclaringType == entity))&lt;br/&gt;    {&lt;br/&gt;        NavigationProperty inverse = ef.Inverse(navProperty);&lt;br/&gt;&lt;br/&gt;        if (inverse != null &amp;amp;&amp;amp; !IsReadWriteAccessibleProperty(inverse))&lt;br/&gt;        {&lt;br/&gt;            inverse = null;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        if ( (inverse != null || ((AssociationType)navProperty.RelationshipType).IsForeignKey) &amp;amp;&amp;amp;&lt;br/&gt;             (navProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many) )&lt;br/&gt;        {&lt;br/&gt;            var skipKeysArgument = (navProperty.GetDependentProperties().Where(p=&amp;gt;ef.IsNullable(p)).Any() &amp;amp;&amp;amp; navProperty.GetDependentProperties().Count() &amp;gt; 1)&lt;br/&gt;                ? &amp;quot;, bool skipKeys = false&amp;quot;&lt;br/&gt;                : String.Empty;&lt;br/&gt;#&amp;gt;&lt;br/&gt;&lt;br/&gt;    private void Fixup&amp;lt;#=navProperty.Name#&amp;gt;(&amp;lt;#=code.Escape(navProperty.ToEndMember.GetEntityType())#&amp;gt; previousValue&amp;lt;#= skipKeysArgument #&amp;gt;)&lt;br/&gt;    {&lt;br/&gt;&amp;lt;#&lt;br/&gt;        if (inverse != null)&lt;br/&gt;        {&lt;br/&gt;            if (inverse.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        if (previousValue != null &amp;amp;&amp;amp; previousValue.&amp;lt;#=code.Escape(inverse)#&amp;gt;.Contains(this))&lt;br/&gt;        {&lt;br/&gt;            previousValue.&amp;lt;#=code.Escape(inverse)#&amp;gt;.Remove(this);&lt;br/&gt;        }&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;            else&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        if (previousValue != null &amp;amp;&amp;amp; ReferenceEquals(previousValue.&amp;lt;#=code.Escape(inverse)#&amp;gt;, this))&lt;br/&gt;        {&lt;br/&gt;            previousValue.&amp;lt;#=code.Escape(inverse)#&amp;gt; = null;&lt;br/&gt;        }&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;&lt;br/&gt;            if (inverse.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;&lt;br/&gt;        if (&amp;lt;#=code.Escape(navProperty)#&amp;gt; != null)&lt;br/&gt;        {&lt;br/&gt;            if (!&amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(inverse)#&amp;gt;.Contains(this))&lt;br/&gt;            {&lt;br/&gt;                &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(inverse)#&amp;gt;.Add(this);&lt;br/&gt;            }&lt;br/&gt;&amp;lt;#&lt;br/&gt;                foreach (var dependentProperty in navProperty.GetDependentProperties())&lt;br/&gt;                {&lt;br/&gt;                    EdmProperty principalProperty = ef.GetCorrespondingPrincipalProperty(navProperty, dependentProperty);&lt;br/&gt;                    if (((PrimitiveType)principalProperty.TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.Binary)&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            if (!StructuralComparisons.StructuralEqualityComparer.Equals(&amp;lt;#=code.Escape(dependentProperty)#&amp;gt;, &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(principalProperty)#&amp;gt;))&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;                    else&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            if (&amp;lt;#=code.Escape(dependentProperty)#&amp;gt; != &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(principalProperty)#&amp;gt;)&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;#&amp;gt;&lt;br/&gt;            {&lt;br/&gt;                &amp;lt;#=code.Escape(dependentProperty)#&amp;gt; = &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(principalProperty)#&amp;gt;;&lt;br/&gt;            }&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;#&amp;gt;&lt;br/&gt;        }&lt;br/&gt;&amp;lt;#&lt;br/&gt;                if (navProperty.GetDependentProperties().Where(p=&amp;gt;ef.IsNullable(p)).Any())&lt;br/&gt;                {&lt;br/&gt;                    if (navProperty.GetDependentProperties().Count() &amp;gt; 1)&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        else if (!_settingFK &amp;amp;&amp;amp; !skipKeys)&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;                    else&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        else if (!_settingFK)&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;#&amp;gt;&lt;br/&gt;        {&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    foreach (var dependentProperty in navProperty.GetDependentProperties().Where(p =&amp;gt; ef.IsNullable(p)))&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            &amp;lt;#=code.Escape(dependentProperty)#&amp;gt; = null;&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;#&amp;gt;&lt;br/&gt;        }&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;            else&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;&lt;br/&gt;        if (&amp;lt;#=code.Escape(navProperty)#&amp;gt; != null)&lt;br/&gt;        {&lt;br/&gt;            &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(inverse)#&amp;gt; = this;&lt;br/&gt;&amp;lt;#&lt;br/&gt;                foreach (var dependentProperty in navProperty.GetDependentProperties())&lt;br/&gt;                {&lt;br/&gt;                    EdmProperty principalProperty = ef.GetCorrespondingPrincipalProperty(navProperty, dependentProperty);&lt;br/&gt;                    if (((PrimitiveType)principalProperty.TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.Binary)&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            if (!StructuralComparisons.StructuralEqualityComparer.Equals(&amp;lt;#=code.Escape(dependentProperty)#&amp;gt;, &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(principalProperty)#&amp;gt;))&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;                    else&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            if (&amp;lt;#=code.Escape(dependentProperty)#&amp;gt; != &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(principalProperty)#&amp;gt;)&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;#&amp;gt;&lt;br/&gt;            {&lt;br/&gt;                &amp;lt;#=code.Escape(dependentProperty)#&amp;gt; = &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(principalProperty)#&amp;gt;;&lt;br/&gt;            }&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;#&amp;gt;&lt;br/&gt;        }&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;        else&lt;br/&gt;        {&lt;br/&gt;            if (navProperty.GetDependentProperties().Any())&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        if (&amp;lt;#=code.Escape(navProperty)#&amp;gt; != null)&lt;br/&gt;        {&lt;br/&gt;&amp;lt;#&lt;br/&gt;                foreach (var dependentProperty in navProperty.GetDependentProperties())&lt;br/&gt;                {&lt;br/&gt;                    EdmProperty principalProperty = ef.GetCorrespondingPrincipalProperty(navProperty, dependentProperty);&lt;br/&gt;                    if (((PrimitiveType)principalProperty.TypeUsage.EdmType).PrimitiveTypeKind == PrimitiveTypeKind.Binary)&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            if (!StructuralComparisons.StructuralEqualityComparer.Equals(&amp;lt;#=code.Escape(dependentProperty)#&amp;gt;, &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(principalProperty)#&amp;gt;))&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;                    else&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            if (&amp;lt;#=code.Escape(dependentProperty)#&amp;gt; != &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(principalProperty)#&amp;gt;)&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;#&amp;gt;&lt;br/&gt;            {&lt;br/&gt;                &amp;lt;#=code.Escape(dependentProperty)#&amp;gt; = &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(principalProperty)#&amp;gt;;&lt;br/&gt;            }&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;#&amp;gt;&lt;br/&gt;        }&lt;br/&gt;&amp;lt;#&lt;br/&gt;                if (navProperty.GetDependentProperties().Where(p =&amp;gt; ef.IsNullable(p)).Any())&lt;br/&gt;                {&lt;br/&gt;                    if (navProperty.GetDependentProperties().Count() &amp;gt; 1)&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        else if (!_settingFK &amp;amp;&amp;amp; !skipKeys)&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;                    else&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        else if (!_settingFK)&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;#&amp;gt;&lt;br/&gt;        {&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    foreach (var dependentProperty in navProperty.GetDependentProperties().Where(p =&amp;gt; ef.IsNullable(p)))&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            &amp;lt;#=code.Escape(dependentProperty)#&amp;gt; = null;&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;#&amp;gt;&lt;br/&gt;        }&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;            else if (((AssociationType)navProperty.RelationshipType).IsForeignKey)&lt;br/&gt;            {&lt;br/&gt;#&amp;gt;&lt;br/&gt;        if (&amp;lt;#=code.Escape(navProperty)#&amp;gt; != null)&lt;br/&gt;        {&lt;br/&gt;&amp;lt;#&lt;br/&gt;                foreach (var fromProperty in ef.GetPrincipalProperties(navProperty))&lt;br/&gt;                {&lt;br/&gt;#&amp;gt;&lt;br/&gt;            &amp;lt;#=code.Escape(navProperty)#&amp;gt;.&amp;lt;#=code.Escape(ef.GetCorrespondingDependentProperty(navProperty, fromProperty))#&amp;gt; = &amp;lt;#=code.Escape(fromProperty)#&amp;gt;;&lt;br/&gt;&amp;lt;#&lt;br/&gt;                }&lt;br/&gt;#&amp;gt;&lt;br/&gt;        }&lt;br/&gt;&amp;lt;#&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;#&amp;gt;&lt;br/&gt;    }&lt;br/&gt;&amp;lt;#&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(np =&amp;gt; np.DeclaringType == entity))&lt;br/&gt;    {&lt;br/&gt;        NavigationProperty inverse = ef.Inverse(navProperty);&lt;br/&gt;&lt;br/&gt;        if (inverse != null &amp;amp;&amp;amp; !IsReadWriteAccessibleProperty(inverse))&lt;br/&gt;        {&lt;br/&gt;            inverse = null;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        if ( (inverse != null || ((AssociationType)navProperty.RelationshipType).IsForeignKey) &amp;amp;&amp;amp;&lt;br/&gt;             (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) )&lt;br/&gt;        {&lt;br/&gt;#&amp;gt;&lt;br/&gt;&lt;br/&gt;    private void Fixup&amp;lt;#=navProperty.Name#&amp;gt;(object sender, NotifyCollectionChangedEventArgs e)&lt;br/&gt;    {&lt;br/&gt;        if (e.NewItems != null)&lt;br/&gt;        {&lt;br/&gt;            foreach (&amp;lt;#=code.Escape(navProperty.ToEndMember.GetEntityType())#&amp;gt; item in e.NewItems)&lt;br/&gt;            {&lt;br/&gt;&amp;lt;#&lt;br/&gt;                if (inverse != null)&lt;br/&gt;                {&lt;br/&gt;                    if (inverse.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many)&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                item.&amp;lt;#=code.Escape(inverse)#&amp;gt; = this;&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;                    else&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                if (!item.&amp;lt;#=code.Escape(inverse)#&amp;gt;.Contains(this))&lt;br/&gt;                {&lt;br/&gt;                    item.&amp;lt;#=code.Escape(inverse)#&amp;gt;.Add(this);&lt;br/&gt;                }&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;                else if (((AssociationType)navProperty.RelationshipType).IsForeignKey)&lt;br/&gt;                {&lt;br/&gt;                    foreach (var fromProperty in ef.GetPrincipalProperties(navProperty))&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                item.&amp;lt;#=code.Escape(ef.GetCorrespondingDependentProperty(navProperty, fromProperty))#&amp;gt; = &amp;lt;#=code.Escape(fromProperty)#&amp;gt;;&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;#&amp;gt;&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        if (e.OldItems != null)&lt;br/&gt;        {&lt;br/&gt;            foreach (&amp;lt;#=code.Escape(navProperty.ToEndMember.GetEntityType())#&amp;gt; item in e.OldItems)&lt;br/&gt;            {&lt;br/&gt;&amp;lt;#&lt;br/&gt;                if (inverse != null)&lt;br/&gt;                {&lt;br/&gt;                    if (inverse.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many)&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                if (ReferenceEquals(item.&amp;lt;#=code.Escape(inverse)#&amp;gt;, this))&lt;br/&gt;                {&lt;br/&gt;                    item.&amp;lt;#=code.Escape(inverse)#&amp;gt; = null;&lt;br/&gt;                }&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;                    else&lt;br/&gt;                    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                if (item.&amp;lt;#=code.Escape(inverse)#&amp;gt;.Contains(this))&lt;br/&gt;                {&lt;br/&gt;                    item.&amp;lt;#=code.Escape(inverse)#&amp;gt;.Remove(this);&lt;br/&gt;                }&lt;br/&gt;&amp;lt;#&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;                else if (((AssociationType)navProperty.RelationshipType).IsForeignKey)&lt;br/&gt;                {&lt;br/&gt;                    foreach (var fromProperty in ef.GetPrincipalProperties(navProperty))&lt;br/&gt;                    {&lt;br/&gt;                        var p = ef.GetCorrespondingDependentProperty(navProperty, fromProperty);&lt;br/&gt;                        if (ef.IsNullable(p.TypeUsage))&lt;br/&gt;                        {&lt;br/&gt;#&amp;gt;&lt;br/&gt;                item.&amp;lt;#=code.Escape(p)#&amp;gt; = null;&lt;br/&gt;&amp;lt;#&lt;br/&gt;                        }&lt;br/&gt;                    }&lt;br/&gt;                }&lt;br/&gt;;#&amp;gt;&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&amp;lt;#&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    region.End();&lt;br/&gt;#&amp;gt;&lt;br/&gt;}&lt;br/&gt;&amp;lt;#&lt;br/&gt;    EndNamespace(namespaceName);&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;foreach (ComplexType complex in ItemCollection.GetItems&amp;lt;ComplexType&amp;gt;().OrderBy(e =&amp;gt; e.Name))&lt;br/&gt;{&lt;br/&gt;    fileManager.StartNewFile(complex.Name + &amp;quot;.cs&amp;quot;);&lt;br/&gt;    BeginNamespace(namespaceName, code);&lt;br/&gt;#&amp;gt;&lt;br/&gt;&amp;lt;#=Accessibility.ForType(complex)#&amp;gt; partial class &amp;lt;#=code.Escape(complex)#&amp;gt;&lt;br/&gt;{&lt;br/&gt;&amp;lt;#&lt;br/&gt;    region.Begin(&amp;quot;Primitive Properties&amp;quot;);&lt;br/&gt;&lt;br/&gt;    foreach(EdmProperty edmProperty in complex.Properties.Where(p =&amp;gt; p.TypeUsage.EdmType is PrimitiveType &amp;amp;&amp;amp; p.DeclaringType == complex))&lt;br/&gt;    {&lt;br/&gt;        bool isDefaultValueDefinedInModel = (edmProperty.DefaultValue != null);&lt;br/&gt;#&amp;gt;&lt;br/&gt;&lt;br/&gt;    &amp;lt;#=Accessibility.ForProperty(edmProperty)#&amp;gt; &amp;lt;#=code.Escape(edmProperty.TypeUsage)#&amp;gt; &amp;lt;#=code.Escape(edmProperty)#&amp;gt;&lt;br/&gt;&amp;lt;#&lt;br/&gt;        if (isDefaultValueDefinedInModel)&lt;br/&gt;        {&lt;br/&gt;#&amp;gt;&lt;br/&gt;    {&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#&amp;gt;get { return &amp;lt;#=code.FieldName(edmProperty)#&amp;gt;; }&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#&amp;gt;set { &amp;lt;#=code.FieldName(edmProperty)#&amp;gt; = value; }&lt;br/&gt;    }&lt;br/&gt;    private &amp;lt;#=code.Escape(edmProperty.TypeUsage)#&amp;gt; &amp;lt;#=code.FieldName(edmProperty)#&amp;gt;&amp;lt;#=code.StringBefore(&amp;quot; = &amp;quot;, code.CreateLiteral(edmProperty.DefaultValue))#&amp;gt;;&lt;br/&gt;&amp;lt;#&lt;br/&gt;        }&lt;br/&gt;        else&lt;br/&gt;        {&lt;br/&gt;#&amp;gt;&lt;br/&gt;    {&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#&amp;gt;get;&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#&amp;gt;set;&lt;br/&gt;    }&lt;br/&gt;&amp;lt;#&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    region.End();&lt;br/&gt;&lt;br/&gt;    region.Begin(&amp;quot;Complex Properties&amp;quot;);&lt;br/&gt;&lt;br/&gt;    foreach(EdmProperty edmProperty in complex.Properties.Where(p =&amp;gt; p.TypeUsage.EdmType is ComplexType &amp;amp;&amp;amp; p.DeclaringType == complex))&lt;br/&gt;    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;&lt;br/&gt;    &amp;lt;#=Accessibility.ForProperty(edmProperty)#&amp;gt; &amp;lt;#=code.Escape(edmProperty.TypeUsage)#&amp;gt; &amp;lt;#=code.Escape(edmProperty)#&amp;gt;&lt;br/&gt;    {&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForGetter(edmProperty))#&amp;gt;get { return &amp;lt;#=code.FieldName(edmProperty)#&amp;gt;; }&lt;br/&gt;        &amp;lt;#=code.SpaceAfter(Accessibility.ForSetter(edmProperty))#&amp;gt;set { &amp;lt;#=code.FieldName(edmProperty)#&amp;gt; = value; }&lt;br/&gt;    }&lt;br/&gt;    private &amp;lt;#=code.Escape(edmProperty.TypeUsage)#&amp;gt; &amp;lt;#=code.FieldName(edmProperty)#&amp;gt; = new &amp;lt;#=code.Escape(edmProperty.TypeUsage)#&amp;gt;();&lt;br/&gt;&amp;lt;#&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    region.End();&lt;br/&gt;#&amp;gt;&lt;br/&gt;}&lt;br/&gt;&amp;lt;#&lt;br/&gt;    EndNamespace(namespaceName);&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;if (!VerifyTypesAreCaseInsensitiveUnique(ItemCollection))&lt;br/&gt;{&lt;br/&gt;    return &amp;quot;&amp;quot;;&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;fileManager.Process();&lt;br/&gt;&lt;br/&gt;#&amp;gt;&lt;br/&gt;&amp;lt;#+&lt;br/&gt;void WriteHeader(EntityFrameworkTemplateFileManager fileManager, params string[] extraUsings)&lt;br/&gt;{&lt;br/&gt;    fileManager.StartHeader();&lt;br/&gt;#&amp;gt;&lt;br/&gt;//------------------------------------------------------------------------------&lt;br/&gt;// &amp;lt;auto-generated&amp;gt;&lt;br/&gt;//     This code was generated from a template.&lt;br/&gt;//&lt;br/&gt;//     Changes to this file may cause incorrect behavior and will be lost if&lt;br/&gt;//     the code is regenerated.&lt;br/&gt;// &amp;lt;/auto-generated&amp;gt;&lt;br/&gt;//------------------------------------------------------------------------------&lt;br/&gt;&lt;br/&gt;using System;&lt;br/&gt;using System.Collections;&lt;br/&gt;using System.Collections.Generic;&lt;br/&gt;using System.Collections.ObjectModel;&lt;br/&gt;using System.Collections.Specialized;&lt;br/&gt;&amp;lt;#=String.Join(String.Empty, extraUsings.Select(u =&amp;gt; &amp;quot;using &amp;quot; + u + &amp;quot;;&amp;quot; + Environment.NewLine).ToArray())#&amp;gt;&lt;br/&gt;&amp;lt;#+&lt;br/&gt;    fileManager.EndBlock();&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;void BeginNamespace(string namespaceName, CodeGenerationTools code)&lt;br/&gt;{&lt;br/&gt;    CodeRegion region = new CodeRegion(this);&lt;br/&gt;    if (!String.IsNullOrEmpty(namespaceName))&lt;br/&gt;    {&lt;br/&gt;#&amp;gt;&lt;br/&gt;namespace &amp;lt;#=code.EscapeNamespace(namespaceName)#&amp;gt;&lt;br/&gt;{&lt;br/&gt;&amp;lt;#+&lt;br/&gt;        PushIndent(CodeRegion.GetIndent(1));&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;void EndNamespace(string namespaceName)&lt;br/&gt;{&lt;br/&gt;    if (!String.IsNullOrEmpty(namespaceName))&lt;br/&gt;    {&lt;br/&gt;        PopIndent();&lt;br/&gt;#&amp;gt;&lt;br/&gt;}&lt;br/&gt;&amp;lt;#+&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;bool IsReadWriteAccessibleProperty(EdmMember member)&lt;br/&gt;{&lt;br/&gt;    string setter = Accessibility.ForWriteOnlyProperty(member);&lt;br/&gt;    string getter = Accessibility.ForReadOnlyProperty(member);&lt;br/&gt;&lt;br/&gt;    return getter != &amp;quot;private&amp;quot; &amp;amp;&amp;amp; getter != &amp;quot;protected&amp;quot; &amp;amp;&amp;amp; setter != &amp;quot;private&amp;quot; &amp;amp;&amp;amp; setter != &amp;quot;protected&amp;quot;;&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;string PropertyVirtualModifier(string accessibility)&lt;br/&gt;{&lt;br/&gt;    return accessibility + (accessibility != &amp;quot;private&amp;quot; ? &amp;quot; virtual&amp;quot; : &amp;quot;&amp;quot;);&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;void WriteCustomObservableCollection()&lt;br/&gt;{&lt;br/&gt;#&amp;gt;&lt;br/&gt;// An System.Collections.ObjectModel.ObservableCollection that raises&lt;br/&gt;// individual item removal notifications on clear and prevents adding duplicates.&lt;br/&gt;public class FixupCollection&amp;lt;T&amp;gt; : ObservableCollection&amp;lt;T&amp;gt;&lt;br/&gt;{&lt;br/&gt;    protected override void ClearItems()&lt;br/&gt;    {&lt;br/&gt;        new List&amp;lt;T&amp;gt;(this).ForEach(t =&amp;gt; Remove(t));&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    protected override void InsertItem(int index, T item)&lt;br/&gt;    {&lt;br/&gt;        if (!this.Contains(item))&lt;br/&gt;        {&lt;br/&gt;            base.InsertItem(index, item);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;&amp;lt;#+&lt;br/&gt;}&lt;br/&gt;&lt;br/&gt;bool VerifyTypesAreCaseInsensitiveUnique(EdmItemCollection itemCollection)&lt;br/&gt;{&lt;br/&gt;    Dictionary&amp;lt;string, bool&amp;gt; alreadySeen = new Dictionary&amp;lt;string, bool&amp;gt;(StringComparer.OrdinalIgnoreCase);&lt;br/&gt;    foreach(StructuralType type in itemCollection.GetItems&amp;lt;StructuralType&amp;gt;())&lt;br/&gt;    {&lt;br/&gt;        if (!(type is EntityType || type is ComplexType))&lt;br/&gt;        {&lt;br/&gt;            continue;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        if (alreadySeen.ContainsKey(type.FullName))&lt;br/&gt;        {&lt;br/&gt;            Error(String.Format(CultureInfo.CurrentCulture, &amp;quot;This template does not support types that differ only by case, the types {0} are not supported&amp;quot;, type.FullName));&lt;br/&gt;            return false;&lt;br/&gt;        }&lt;br/&gt;        else&lt;br/&gt;        {&lt;br/&gt;            alreadySeen.Add(type.FullName, true);&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    return true;&lt;br/&gt;}&lt;br/&gt;#&amp;gt;&lt;p&gt;看起来很长？其实主要内容并不复杂。我们所要关心的的Entity生成代码都在一个遍历所有entity的foreach中，对于每个entity，它依次枚举Primitive Properties - entity本身定义的基本类型字段，Complex Properties - 用户定义的复杂类型字段，以及Navigation Properties - entity的关系字段。&lt;/p&gt;&lt;p&gt;首先找到生成每个类定义的代码：&lt;/p&gt;&amp;lt;#=Accessibility.ForType(entity)#&amp;gt; &amp;lt;#=code.SpaceAfter(code.AbstractOption(entity))#&amp;gt;partial class &amp;lt;#=code.Escape(entity)#&amp;gt;&amp;lt;#=code.StringBefore(&amp;quot; : &amp;quot;, code.Escape(entity.BaseType))#&amp;gt;&lt;p&gt;在上面加一行：&lt;/p&gt;[DataContract(Name = &amp;quot;&amp;lt;#=char.ToLowerInvariant(entity.Name[0]) + entity.Name.Substring(1) #&amp;gt;&amp;quot;)]&lt;p&gt;然后再找到生成Primitive Property的代码（其他的类似）：&lt;/p&gt;&amp;lt;#=PropertyVirtualModifier(Accessibility.ForProperty(edmProperty))#&amp;gt; &amp;lt;#=code.Escape(edmProperty.TypeUsage)#&amp;gt; &amp;lt;#=code.Escape(edmProperty)#&amp;gt;&lt;p&gt;在上面加一行：&lt;/p&gt;[DataMember(Name = &amp;quot;&amp;lt;#=char.ToLowerInvariant(edmProperty.Name[0]) + edmProperty.Name.Substring(1) #&amp;gt;&amp;quot;)]&lt;p&gt;由于DataContract在System.Runtime.Serialization命名空间下，我们还需要添加命名空间。在接近Model.tt末尾的地方找到有一堆命名空间定义，在末尾加上：&lt;/p&gt;using System.Runtime.Serialization;&lt;p&gt;然后Save，这时代码会重新生成，我们再看一下这时的Microblog.cs:&lt;/p&gt;//------------------------------------------------------------------------------&lt;br/&gt;// &amp;lt;auto-generated&amp;gt;&lt;br/&gt;//     This code was generated from a template.&lt;br/&gt;//&lt;br/&gt;//     Changes to this file may cause incorrect behavior and will be lost if&lt;br/&gt;//     the code is regenerated.&lt;br/&gt;// &amp;lt;/auto-generated&amp;gt;&lt;br/&gt;//------------------------------------------------------------------------------&lt;br/&gt;&lt;br/&gt;using System;&lt;br/&gt;using System.Collections;&lt;br/&gt;using System.Collections.Generic;&lt;br/&gt;using System.Collections.ObjectModel;&lt;br/&gt;using System.Collections.Specialized;&lt;br/&gt;using System.Runtime.Serialization;&lt;br/&gt;&lt;br/&gt;namespace WcfRestServiceDemo.Data&lt;br/&gt;{&lt;br/&gt;    [DataContract(Name = &amp;quot;microblog&amp;quot;)]&lt;br/&gt;    public partial class Microblog&lt;br/&gt;    {&lt;br/&gt;        #region Primitive Properties&lt;br/&gt;        [DataMember(Name = &amp;quot;id&amp;quot;)]&lt;br/&gt;        public virtual int Id&lt;br/&gt;        {&lt;br/&gt;            get;&lt;br/&gt;            set;&lt;br/&gt;        }&lt;br/&gt;        [DataMember(Name = &amp;quot;content&amp;quot;)]&lt;br/&gt;        public virtual string Content&lt;br/&gt;        {&lt;br/&gt;            get;&lt;br/&gt;            set;&lt;br/&gt;        }&lt;br/&gt;        [DataMember(Name = &amp;quot;publishTime&amp;quot;)]&lt;br/&gt;        public virtual System.DateTime PublishTime&lt;br/&gt;        {&lt;br/&gt;            get;&lt;br/&gt;            set;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        #endregion&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;这时如果测试一下服务，就可以看到不论是XML还是JSON的返回结果中的字段都已经变成小写字母开头了。&lt;/p&gt;&lt;p&gt;需要注意的是，不仅是返回，这时客户端发送请求时，对应字段也必须同样改成小写字母开头。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;  &lt;hr /&gt;处理Null值属性&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;为了引入这个问题，我们将服务稍稍变得复杂一些。假设现在并且每条微博可以关联一张图片，用户只要在写微博同时，输入图片地址即可。那么修改Microblog模型，添加Picture属性，包含一个图片的URL。由于图片是可选的，所以还要将的Nullable设为true。&lt;/p&gt;&lt;p&gt;如下：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201104/201104201044129063.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201104/201104201044126206.png" width="155" height="186" /&gt;&lt;/a&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201104/201104201044139554.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201104/2011042010441377.png" width="277" height="70" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;由于修改了EntityModel，需要在两个.tt模板文件上右键选择Run Custom Tools来生成新的POCO entity.&lt;/p&gt;&lt;p&gt;然后我们看一下服务的返回：&lt;/p&gt;&amp;lt;ArrayOfmicroblog xmlns=&amp;quot;http://schemas.datacontract.org/2004/07/WcfRestServiceDemo.Data&amp;quot; xmlns:i=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&amp;gt;&lt;br/&gt;  &amp;lt;microblog&amp;gt;&lt;br/&gt;    &amp;lt;content&amp;gt;Microblog with no picture.&amp;lt;/content&amp;gt;&lt;br/&gt;    &amp;lt;id&amp;gt;1&amp;lt;/id&amp;gt;&lt;br/&gt;    &amp;lt;picture i:nil=&amp;quot;true&amp;quot;/&amp;gt;&lt;br/&gt;    &amp;lt;publishTime&amp;gt;2011-04-19T22:00:00&amp;lt;/publishTime&amp;gt;&lt;br/&gt;  &amp;lt;/microblog&amp;gt;&lt;br/&gt;  &amp;lt;microblog&amp;gt;&lt;br/&gt;    &amp;lt;content&amp;gt;Microblog with picture&amp;lt;/content&amp;gt;&lt;br/&gt;    &amp;lt;id&amp;gt;2&amp;lt;/id&amp;gt;&lt;br/&gt;    &amp;lt;picture&amp;gt;http://somepicture/somepicture&amp;lt;/picture&amp;gt;&lt;br/&gt;    &amp;lt;publishTime&amp;gt;2011-04-19T22:00:00&amp;lt;/publishTime&amp;gt;&lt;br/&gt;  &amp;lt;/microblog&amp;gt;&lt;br/&gt;&amp;lt;/ArrayOfmicroblog&amp;gt;&lt;p&gt;这里有两条microblog，一条有picture，一条没有。但是没有picture的那个也把picture字段给返回了，只是用i:nil=”true”来标识这是个null值。显然这不是理想方式，想象一下一个entity可能会有很多可空的属性，尤其是当一个entity与许多其他entity产生关联，引入了许多navigation properties时，传输这些空属性将严重影响可读性（虽然大多数情况下应该不是人来读）并对性能造成影响（传输、序列化和反序列化）。（&lt;strong&gt;当然这不是绝对的，nil值有其应用场景，请根据实际情况决定&lt;/strong&gt;）&lt;/p&gt;&lt;p&gt;幸好DataMember attribute还有一个EmitDefaultValue属性，可以指定是否要序列化默认值。用和刚才类似的方法，修改T4模板，修改Property定义上方的[DataMember]:&lt;/p&gt;    [DataMember(Name = &amp;quot;&amp;lt;#=char.ToLowerInvariant(edmProperty.Name[0]) + edmProperty.Name.Substring(1) #&amp;gt;&amp;quot;&amp;lt;#&lt;br/&gt;if(edmProperty.Nullable)&lt;br/&gt;{&lt;br/&gt;#&amp;gt;, EmitDefaultValue = false&amp;lt;#&lt;br/&gt;}&lt;br/&gt;#&amp;gt;)]首先判断了property是否可设为null，若可以才省略空值。否则对于一个int属性，值为0时它就不会被传输了，这一般是不太合适的。（确切的讲，这里的Nullable和数据类型没有关系，指的是在数据模型中的nullable，也就是是否为可空字段。) &lt;p&gt;再次访问服务：&lt;/p&gt;&amp;lt;ArrayOfmicroblog xmlns=&amp;quot;http://schemas.datacontract.org/2004/07/WcfRestServiceDemo.Data&amp;quot; xmlns:i=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&amp;gt;&lt;br/&gt;  &amp;lt;microblog&amp;gt;&lt;br/&gt;    &amp;lt;content&amp;gt;Microblog with no picture.&amp;lt;/content&amp;gt;&lt;br/&gt;    &amp;lt;id&amp;gt;1&amp;lt;/id&amp;gt;&lt;br/&gt;    &amp;lt;publishTime&amp;gt;2011-04-19T22:00:00&amp;lt;/publishTime&amp;gt;&lt;br/&gt;  &amp;lt;/microblog&amp;gt;&lt;br/&gt;  &amp;lt;microblog&amp;gt;&lt;br/&gt;    &amp;lt;content&amp;gt;Microblog with picture&amp;lt;/content&amp;gt;&lt;br/&gt;    &amp;lt;id&amp;gt;2&amp;lt;/id&amp;gt;&lt;br/&gt;    &amp;lt;picture&amp;gt;http://somepicture/somepicture&amp;lt;/picture&amp;gt;&lt;br/&gt;    &amp;lt;publishTime&amp;gt;2011-04-19T22:00:00&amp;lt;/publishTime&amp;gt;&lt;br/&gt;  &amp;lt;/microblog&amp;gt;&lt;br/&gt;&amp;lt;/ArrayOfmicroblog&amp;gt;&lt;p&gt;可以看到没有图片的那个microblog的picture属性已经被省略了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;  &lt;hr /&gt;JSON中的DateTime&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在第二章末尾我们提到过DateTime属性在JSON表示下的奇怪格式。严格来说，这不算什么问题，因为既然&lt;a href="http://weblogs.asp.net/bleroy/archive/2008/01/18/dates-and-json.aspx" target="_blank"&gt;微软选择了这种格式&lt;/a&gt;，那么全世界的人都在碰到这个问题，相应的客户端解决方案也有很多。比如：&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;&lt;a href="http://archive.cnblogs.com/a/1444633/"&gt;http://archive.cnblogs.com/a/1444633/&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://stackoverflow.com/questions/206384/how-to-format-json-date"&gt;http://stackoverflow.com/questions/206384/how-to-format-json-date&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;如果一定要从服务端解决这个问题的话，最根本的方法是改用自定义的序列化器。而比较简单的方法是为entity写一个partial类，然后提供一个long格式的对应属性：&lt;/p&gt;using System;&lt;br/&gt;using System.Runtime.Serialization;&lt;br/&gt;&lt;br/&gt;namespace WcfRestServiceDemo.Data&lt;br/&gt;{&lt;br/&gt;    partial class Microblog&lt;br/&gt;    {&lt;br/&gt;        private static readonly DateTime _baseTime =&lt;br/&gt;            new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime();&lt;br/&gt;&lt;br/&gt;        [DataMember(Name = &amp;quot;publishTimeSec&amp;quot;)]&lt;br/&gt;        public long PublishTimeSeconds&lt;br/&gt;        {&lt;br/&gt;            get { return (long)(PublishTime - _baseTime).TotalMilliseconds; }&lt;br/&gt;            set { PublishTime = _baseTime.AddMilliseconds(value); }&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;这时服务的返回就是：&lt;/p&gt;[&lt;br/&gt; {&lt;br/&gt;  &amp;quot;content&amp;quot;:&amp;quot;Microblog with no picture.&amp;quot;,&lt;br/&gt;  &amp;quot;id&amp;quot;:1,&lt;br/&gt;  &amp;quot;publishTime&amp;quot;:&amp;quot;\/Date(1303221600000+0800)\/&amp;quot;,&lt;br/&gt;  &amp;quot;publishTimeSec&amp;quot;:1303221600000&lt;br/&gt; },&lt;br/&gt; {&lt;br/&gt;  &amp;quot;content&amp;quot;:&amp;quot;Microblog with picture&amp;quot;,&lt;br/&gt;  &amp;quot;id&amp;quot;:2,&lt;br/&gt;  &amp;quot;picture&amp;quot;:&amp;quot;http:\/\/somepicture\/somepicture&amp;quot;,&lt;br/&gt;  &amp;quot;publishTime&amp;quot;:&amp;quot;\/Date(1303221600000+0800)\/&amp;quot;,&lt;br/&gt;  &amp;quot;publishTimeSec&amp;quot;:1303221600000&lt;br/&gt; }&lt;br/&gt;]&lt;p&gt;当然更绝的做法是把这个替代属性的生成也交给T4模板来做。具体做法请参考我&lt;a href="http://files.cnblogs.com/Gildor/WcfRestServiceDemo.ch4.7z" target="_blank"&gt;整理好后上传的代码&lt;/a&gt;。&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;&lt;strong&gt;小结&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;本文介绍了:&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;如何修改T4模板控制模型代码的生成，包括修改Type名称、属性名称的起始字母大小写和是否要序列化默认值的选项 &lt;/li&gt;  &lt;li&gt;如何用替代属性解决JSON中DateTime格式的问题。 &lt;/li&gt;&lt;/ul&gt;&lt;hr /&gt;&lt;strong&gt;我的WCF4 REST Service及Entity Framework with POCO之旅系列&lt;/strong&gt; &lt;ul&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/28/1997426.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（一）——创建一个基本的RESTful Service&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/29/1998392.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（二）——选择请求/返回格式&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/31/2000553.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（三）——用Entity Framework和POCO Template实现数据模型及存储&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/04/20/2022013.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（四）——定制Entity&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/08/22/2148804.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（五）——身份验证&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://www.cnblogs.com/Gildor/aggbug/2022013.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/04/20/2022013.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Gildor/archive/2011/03/31/2000553.html</id><title type="text">我的WCF4 Rest Service及Entity Framework with POCO之旅（三）——用Entity Framework和POCO Template实现数据模型及存储</title><summary type="text">既然这个系列标题中都含有Entity Framework和POCO，这两者若到第三篇还不出现那就太奇怪了。本文将介绍如何使用Entity Framework和POCO来实现数据模型的创建以及数据存储。</summary><published>2011-03-31T01:04:00Z</published><updated>2011-03-31T01:04:00Z</updated><author><name>Gildor Wang</name><uri>http://www.cnblogs.com/Gildor/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Gildor/archive/2011/03/31/2000553.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Gildor/archive/2011/03/31/2000553.html"/><content type="html">&lt;p&gt;既然这个系列标题中都含有Entity Framework和POCO，这两者若到第三篇还不出现那就太奇怪了。本文将介绍如何使用Entity Framework和POCO来实现数据模型的创建以及数据存储。&lt;/p&gt;  &lt;p&gt;接着上文，我们现在已经有了一个微博服务。但是，一旦网站重启或者IIS重启，我们就会丢失之前所有发布的微博，这样的服务显然不是我们所能接受的，我们接下来要构建服务的数据存储。使用Entity Framework的Model-First设计方式，可以大大简化这个过程。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;使用Entity Data Model创建数据模型&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;我们首先创建一个类库项目WcfRestServiceDemo.Data，然后添加新项“ADO.NET Entity Data Model”：    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903158857.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903179215.png" width="644" height="446" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;选择数据源，如果有现成的数据源的话可以从数据库导入。我们选择Empty model, 新创建数据模型。    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903181068.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903195048.png" width="543" height="484" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;从工具箱中找到Entity，拖入设计器，参考之前创建的数据类型，修改得到：    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903205854.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903208296.png" width="221" height="220" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;到这里数据模型就创建完了。下面我们要把它同步到SQL Server。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;由模型生成数据库&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;到目前为止，模型还只存在C#项目中，虽然我们已经可以写代码来操纵数据，但实际上是无法运行的（或者说一运行就会挂掉的）。&lt;/p&gt;  &lt;p&gt;要将模型同步到SQL Server，对设计器视图点右键，选择Generate Database from Model…    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903214326.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903227815.png" width="325" height="315" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;选择一个连接，没有的话就创建一个：    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903235208.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903252817.png" width="501" height="484" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;然后对应的SQL脚本就自动生成好了：    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/20110331090326210.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/20110331090327601.png" width="501" height="484" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;打开WcfRestServiceDemo.edmx.sql，右键选择Execute SQL，成功后模型就同步到SQL Server了。    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903284962.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903303958.png" width="573" height="484" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;修改服务代码&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;现在数据模型和数据存储都做好了，我们要修改服务代码来使用新的数据模型。&lt;/p&gt;  &lt;p&gt;删去Microblog.cs，添加对WcfRestServiceDemo.Data和System.Data.Entity的引用，并修改MicroblogService.cs代码如下：&lt;/p&gt;  using System;&lt;br/&gt;using System.Collections.Generic;&lt;br/&gt;using System.Linq;&lt;br/&gt;using System.ServiceModel;&lt;br/&gt;using System.ServiceModel.Activation;&lt;br/&gt;using System.ServiceModel.Web;&lt;br/&gt;using WcfRestServiceDemo.Data;&lt;br/&gt; &lt;br/&gt;namespace WcfRestServiceDemo.Service&lt;br/&gt;{&lt;br/&gt;    [ServiceContract(Namespace = &amp;quot;WcfRestServiceDemo&amp;quot;)]&lt;br/&gt;    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]&lt;br/&gt;    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]&lt;br/&gt;    public class MicroblogService&lt;br/&gt;    {&lt;br/&gt;        [WebGet(UriTemplate = &amp;quot;&amp;quot;)]&lt;br/&gt;        public List&amp;lt;Microblog&amp;gt; GetCollection()&lt;br/&gt;        {&lt;br/&gt;            using(var container = new WcfRestServiceDemoContainer())&lt;br/&gt;            {&lt;br/&gt;                return container.Microblogs.ToList();&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt; &lt;br/&gt;        [WebInvoke(UriTemplate = &amp;quot;&amp;quot;, Method = &amp;quot;POST&amp;quot;)]&lt;br/&gt;        public Microblog Create(Microblog microblog)&lt;br/&gt;        {&lt;br/&gt;            microblog.PublishTime = DateTime.Now;&lt;br/&gt;            using(var container = new WcfRestServiceDemoContainer())&lt;br/&gt;            {&lt;br/&gt;                container.Microblogs.AddObject(microblog);&lt;br/&gt;                container.SaveChanges();&lt;br/&gt;            }&lt;br/&gt;            return microblog;&lt;br/&gt;        }&lt;br/&gt; &lt;br/&gt;        [WebGet(UriTemplate = &amp;quot;{id}&amp;quot;)]&lt;br/&gt;        public Microblog Get(string id)&lt;br/&gt;        {&lt;br/&gt;            using(var container = new WcfRestServiceDemoContainer())&lt;br/&gt;            {&lt;br/&gt;                return container.Microblogs.FirstOrDefault(m =&amp;gt; m.Id == int.Parse(id));&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt; &lt;br/&gt;        [WebInvoke(UriTemplate = &amp;quot;{id}&amp;quot;, Method = &amp;quot;DELETE&amp;quot;)]&lt;br/&gt;        public void Delete(string id)&lt;br/&gt;        {&lt;br/&gt;            using(var container = new WcfRestServiceDemoContainer())&lt;br/&gt;            {&lt;br/&gt;                container.Microblogs.DeleteObject(&lt;br/&gt;                    container.Microblogs.First(m =&amp;gt; m.Id == int.Parse(id)));&lt;br/&gt;                container.SaveChanges();&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;  &lt;br /&gt;要让Entity Framework正常工作，我们还需要把它生成的app.config文件中的ConnectionString复制到服务的web.config文件中：&lt;/p&gt;&amp;lt;add name=&amp;quot;WcfRestServiceDemoContainer&amp;quot; connectionString=&amp;quot;metadata=res://*/WcfRestServiceDemo.csdl|res://*/WcfRestServiceDemo.ssdl|res://*/WcfRestServiceDemo.msl;provider=System.Data.SqlClient;provider connection string=&amp;amp;quot;Data Source=.;Initial Catalog=WcfRestServiceDemo;Integrated Security=True;MultipleActiveResultSets=True&amp;amp;quot;&amp;quot; providerName=&amp;quot;System.Data.EntityClient&amp;quot; /&amp;gt;&lt;p&gt;这里附带一提，有的时候我们会遇到无法正确加载Model的情形，这时只要将上面那个连接字符串中的*替换成数据模型(*.edmx)所在的程序集名称（这里是WcfRestServiceDemo.Data)，显式指定搜索路径即可。&lt;/p&gt;&lt;font color="#0000ff" size="3" face="Comic Sans MS"&gt;&lt;span&gt;&lt;/span&gt;&lt;/font&gt;好，都改好了，现在测试一下服务，我先手工往数据库中插入一条记录，然后访问&lt;br /&gt;&lt;a href="http://localhost:6421/microblogs/"&gt;http://localhost:6421/microblogs/&lt;/a&gt;：&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903306400.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903317206.png" width="583" height="277" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;怎么回事？Content, Id, PublishTime这些属性倒是一个也不少，但是也多了很多其他的东西。这是因为与POCO不同，基于EntityObject的Entity Data Model支持更改提醒、关系管理等许多其他功能，所以会包含更多的属性，只不过在我们这个应用场景中，并不需要这些支持。另一个问题是，这时默认的命名空间变成了   &lt;br /&gt;xmlns=http://schemas.datacontract.org/2004/07/WcfRestServiceDemo.Data   &lt;br /&gt;而且由于Microblog这个DataModel是自动生成的，我没有办法改它的DataContractAttribute（改了下次生成又会变回来）:&lt;/p&gt;[EdmEntityTypeAttribute(NamespaceName=&amp;quot;WcfRestServiceDemo&amp;quot;, Name=&amp;quot;Microblog&amp;quot;)]&lt;br/&gt;[Serializable()]&lt;br/&gt;[DataContractAttribute(IsReference=true)]&lt;br/&gt;public partial class Microblog : EntityObject&lt;br/&gt;{&lt;br/&gt;    …&lt;br/&gt;}&lt;p&gt;要解决上述问题，我们该请出下一位主角POCO了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;使用ADO.NET C# POCO Entity Generator生成POCO模型&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;POCO全称Plain-Old CLR Objects（中文不知道怎么说）。以前有很多人手写POCO模型，不过现在已经有了一个非常强大的基于T4模板的模型生成器。&lt;/p&gt;&lt;p&gt;打开Visual Studio 2010的Extension Manager，找到ADO.NET C# POCO Entity Generator并安装（或者在&lt;a href="http://visualstudiogallery.msdn.microsoft.com/23df0450-5677-4926-96cc-173d02752313/" target="_blank"&gt;这里&lt;/a&gt;下载）：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903332515.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903356428.png" width="644" height="446" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;然后打开WcfRestServiceDemo.edmx，右键选择Add Code Generation Item…:   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903362774.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903374627.png" width="326" height="315" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;找到刚才安装的模板：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903385018.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903402802.png" width="644" height="446" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;创建结果：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903419148.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903425669.png" width="278" height="272" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Model.Context.tt和Model.tt分别是Container和Entity的T4模板。现在再运行一下服务，获取所有微博：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/2011033109034363.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/20110331090344520.png" width="644" height="187" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;为什么会Empty Response? 调试一下服务，发现返回的类型不是Microblog, 而是System.Data.Entity.DynamicProxies.Microblog_  &lt;br /&gt;C60A574FC06ABEF4672858332CE687DDE70D188AD3BD73ED2731E8854D30C927，而WCF并不认识这个类型。这是因为默认的Entity Data Model启用了LazyLoading和Proxy机制的缘故。这两个特性我以后也许会详细讲，不过目前我们只要把它们关闭就好。虽然Model设计器提供了下面这个属性可以修改：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903443486.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903467781.png" width="341" height="324" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;但是当我改成False然后对Model.Context.tt右键选择Run Custom Tool 重新生成Container代码，却没有任何效果，Container构造器代码依旧如下：&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903461618.png"&gt;     &lt;br /&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903482533.png" width="546" height="437" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;如果有哪位高手知道应该如何设置，请告诉我。我们现在先通过扩展Container类的方法来解决这个问题，下一篇讲如何通过修改T4模板来改变生成的Container。&lt;/p&gt;&lt;p&gt;在WcfRestServiceDemo.Data中创建WcfRestServiceDemoContainer.cs，代码如下：   &lt;br /&gt;&lt;/p&gt;using System;&lt;br/&gt;&lt;br/&gt;namespace WcfRestServiceDemo.Data&lt;br/&gt;{&lt;br/&gt;    partial class WcfRestServiceDemoContainer&lt;br/&gt;    {&lt;br/&gt;        public static void Go(Action&amp;lt;WcfRestServiceDemoContainer&amp;gt; todo, &lt;br/&gt;            bool isLazy = false)&lt;br/&gt;        {&lt;br/&gt;            Go&amp;lt;object&amp;gt;(entities =&amp;gt;&lt;br/&gt;            {&lt;br/&gt;                todo(entities);&lt;br/&gt;                return null;&lt;br/&gt;            },&lt;br/&gt;            isLazy);&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public static void GoAndSave(Action&amp;lt;WcfRestServiceDemoContainer&amp;gt; todo, &lt;br/&gt;            bool isLazy = false)&lt;br/&gt;        {&lt;br/&gt;            Go&amp;lt;object&amp;gt;(entities =&amp;gt;&lt;br/&gt;            {&lt;br/&gt;                todo(entities);&lt;br/&gt;                entities.SaveChanges();&lt;br/&gt;                return null;&lt;br/&gt;            },&lt;br/&gt;            isLazy);&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public static T Go&amp;lt;T&amp;gt;(Func&amp;lt;WcfRestServiceDemoContainer, T&amp;gt; todo, &lt;br/&gt;            bool isLazy = false)&lt;br/&gt;        {&lt;br/&gt;            using(var entities = new WcfRestServiceDemoContainer())&lt;br/&gt;            {&lt;br/&gt;                entities.ContextOptions.LazyLoadingEnabled = isLazy;&lt;br/&gt;                entities.ContextOptions.ProxyCreationEnabled = false;&lt;br/&gt;                return todo(entities);&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public static T GoAndSave&amp;lt;T&amp;gt;(Func&amp;lt;WcfRestServiceDemoContainer, T&amp;gt; todo, &lt;br/&gt;            bool isLazy = false)&lt;br/&gt;        {&lt;br/&gt;            return Go(entities =&amp;gt;&lt;br/&gt;            {&lt;br/&gt;                var result = todo(entities);&lt;br/&gt;                entities.SaveChanges();&lt;br/&gt;                return result;&lt;br/&gt;            },&lt;br/&gt;            isLazy);&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;这里我顺便写了几个帮助方法，因为每次使用Container时写using很麻烦，关键是我经常改完数据忘记调SaveChanges().&lt;/p&gt;&lt;p&gt;最后修改MicroblogService.cs代码如下：   &lt;br /&gt;&lt;/p&gt;using System;&lt;br/&gt;using System.Collections.Generic;&lt;br/&gt;using System.Linq;&lt;br/&gt;using System.ServiceModel;&lt;br/&gt;using System.ServiceModel.Activation;&lt;br/&gt;using System.ServiceModel.Web;&lt;br/&gt;using WcfRestServiceDemo.Data;&lt;br/&gt;&lt;br/&gt;namespace WcfRestServiceDemo.Service&lt;br/&gt;{&lt;br/&gt;    [ServiceContract(Namespace = &amp;quot;WcfRestServiceDemo&amp;quot;)]&lt;br/&gt;    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]&lt;br/&gt;    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]&lt;br/&gt;    public class MicroblogService&lt;br/&gt;    {&lt;br/&gt;        [WebGet(UriTemplate = &amp;quot;&amp;quot;)]&lt;br/&gt;        public List&amp;lt;Microblog&amp;gt; GetCollection()&lt;br/&gt;        {&lt;br/&gt;            return WcfRestServiceDemoContainer.Go(container =&amp;gt;&lt;br/&gt;                container.Microblogs.ToList());&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        [WebInvoke(UriTemplate = &amp;quot;&amp;quot;, Method = &amp;quot;POST&amp;quot;)]&lt;br/&gt;        public Microblog Create(Microblog microblog)&lt;br/&gt;        {&lt;br/&gt;            microblog.PublishTime = DateTime.Now;&lt;br/&gt;            WcfRestServiceDemoContainer.GoAndSave(container =&amp;gt;&lt;br/&gt;                container.Microblogs.AddObject(microblog));&lt;br/&gt;            return microblog;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        [WebGet(UriTemplate = &amp;quot;{id}&amp;quot;)]&lt;br/&gt;        public Microblog Get(string id)&lt;br/&gt;        {&lt;br/&gt;            return WcfRestServiceDemoContainer.Go(container =&amp;gt;&lt;br/&gt;                container.Microblogs.FirstOrDefault(m =&amp;gt; m.Id == int.Parse(id)));&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        [WebInvoke(UriTemplate = &amp;quot;{id}&amp;quot;, Method = &amp;quot;DELETE&amp;quot;)]&lt;br/&gt;        public void Delete(string id)&lt;br/&gt;        {&lt;br/&gt;            WcfRestServiceDemoContainer.GoAndSave(container =&amp;gt;&lt;br/&gt;                container.Microblogs.DeleteObject(&lt;br/&gt;                    container.Microblogs.First(m =&amp;gt; m.Id == int.Parse(id))));&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;再次访问&lt;a href="http://localhost:6421/microblogs/"&gt;http://localhost:6421/microblogs/&lt;/a&gt;：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903495847.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103310903491321.png" width="440" height="157" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;啊哈，又回到最初干净的状态了。只是Namespace问题以及属性名称的大小写问题还是没有解决，但是我们已经离目标更近一步了，我将在下一章讨论这些。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;小结&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;本文介绍了：&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;如何用Entity Framework的Model-First方式构建数据模型及数据存储 &lt;/li&gt;  &lt;li&gt;如何改用POCO实现数据模型 &lt;/li&gt;  &lt;li&gt;如何关闭LazyLoading和ProxyCreation &lt;/li&gt;  &lt;li&gt;以及一点点代码的sugar &lt;/li&gt;&lt;/ul&gt;&lt;hr /&gt;&lt;strong&gt;我的WCF4 REST Service及Entity Framework with POCO之旅系列&lt;/strong&gt; &lt;ul&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/28/1997426.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（一）——创建一个基本的RESTful Service&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/29/1998392.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（二）——选择请求/返回格式&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/31/2000553.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（三）——用Entity Framework和POCO Template实现数据模型及存储&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/04/20/2022013.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（四）——定制Entity&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/08/22/2148804.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（五）——身份验证&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://www.cnblogs.com/Gildor/aggbug/2000553.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/31/2000553.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Gildor/archive/2011/03/29/1998392.html</id><title type="text">我的WCF4 Rest Service及Entity Framework with POCO之旅（二）——选择请求/返回格式</title><summary type="text">REST被推崇，一个很重要的原因就是它的数据格式可以是XML，也可以是JSON，这使得Javascript可以轻松调用RESTful服务。WCF REST内置了JSON序列化器，可以轻易实现格式的转换，甚至可以根据请求动态选择返回的格式。本文将介绍如何实现手动/自动的格式转换。</summary><published>2011-03-29T01:07:00Z</published><updated>2011-03-29T01:07:00Z</updated><author><name>Gildor Wang</name><uri>http://www.cnblogs.com/Gildor/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Gildor/archive/2011/03/29/1998392.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Gildor/archive/2011/03/29/1998392.html"/><content type="html">&lt;p&gt;REST被推崇，一个很重要的原因就是它的数据格式可以是XML，也可以是JSON，这使得Javascript可以轻松调用RESTful服务。WCF REST内置了JSON序列化器，可以轻易实现格式的转换，甚至可以根据请求动态选择返回的格式。&lt;/p&gt;  &lt;p&gt;先看看如何手动指定返回格式。WebGetAttribute和WebInvokeAttribute有RequesetFormat和ReponseFormat两个属性，可以指定请求/返回格式为XML或者JSON:    &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103290906183101.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image_thumb43" border="0" alt="image_thumb43" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/20110329090620427.png" width="644" height="188" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;但相比之下，动态选择格式还是更灵活也吸引人一些。打开之前创建的项目，在自动生成的web.config中，有这么一条配置：&lt;/p&gt;  &amp;lt;standardEndpoint name=&amp;quot;&amp;quot; helpEnabled=&amp;quot;true&amp;quot; automaticFormatSelectionEnabled=&amp;quot;true&amp;quot;/&amp;gt;&lt;p&gt;设定标准Endpoint为打开&lt;strong&gt;服务帮助&lt;/strong&gt;，并且打开&lt;strong&gt;自动格式选择&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;打开&lt;strong&gt;服务帮助&lt;/strong&gt;就是当访问/microblogs/help时会看到如下的帮助界面：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103290906215802.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image_thumb35" border="0" alt="image_thumb35" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103290906233129.png" width="626" height="301" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;列举了服务的接口，每个接口还说明了其详细用法、示例，以及请求/返回数据的scheme。&lt;/p&gt;&lt;p&gt;而&lt;strong&gt;自动格式选择&lt;/strong&gt;，分为两部分：自动根据请求，选择请求内容的格式和回复内容的格式。这两者分别是通过HTTP请求头中的Content-Type和Accept这两项指定的。&lt;/p&gt;&lt;p&gt;在Fiddler2中把&lt;strong&gt;Content-Type&lt;/strong&gt;改成&lt;strong&gt;application/json&lt;/strong&gt;, 表示请求的内容格式是JSON，再次发布一条微博：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103290906268371.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image_thumb37" border="0" alt="image_thumb37" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103290906281205.png" width="625" height="473" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;请求成功，可以看到请求内容是JSON格式，服务器成功处理了，但返回还是XML格式：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103290906304910.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image_thumb38" border="0" alt="image_thumb38" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103290906322204.png" width="644" height="364" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;现在再修改&lt;strong&gt;Accept&lt;/strong&gt;头为&lt;strong&gt;application/json&lt;/strong&gt;, 表示只接受JSON的返回格式，然后获取所有的微博：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103290906339215.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image_thumb39" border="0" alt="image_thumb39" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103290906357414.png" width="621" height="442" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;结果如下：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103290906375787.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image_thumb40" border="0" alt="image_thumb40" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103290906396112.png" width="644" height="364" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;可以看到，返回内容已经是JSON格式了。等等，那个时间的格式好像有点怪？另外，大写的field名称似乎也不合习惯。别急，我们以后会解决这些问题。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;小结&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;本文介绍了&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;如何手动指定请求/返回的数据格式 &lt;/li&gt;  &lt;li&gt;如何使用WCF的自动格式选择 &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;和上一篇一样，有图有真相。不过也很花时间，我怀疑是不是写得太详细了？以后如非必要，估计不这么写了，毕竟我的目的不是出一套教材，而是希望能整理一些其他地方找不到的东西，否则舍本逐末，得不偿失。 &lt;/p&gt;&lt;hr /&gt;&lt;strong&gt;我的WCF4 REST Service及Entity Framework with POCO之旅系列&lt;/strong&gt; &lt;ul&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/28/1997426.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（一）——创建一个基本的RESTful Service&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/29/1998392.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（二）——选择请求/返回格式&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/31/2000553.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（三）——用Entity Framework和POCO Template实现数据模型及存储&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/04/20/2022013.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（四）——定制Entity&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/08/22/2148804.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（五）——身份验证&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://www.cnblogs.com/Gildor/aggbug/1998392.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/29/1998392.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Gildor/archive/2011/03/28/1997426.html</id><title type="text">我的WCF4 Rest Service及Entity Framework with POCO之旅（一）——创建一个基本的RESTful Service</title><summary type="text">很久没有玩WCF了，年前和几个朋友做一个Restful的服务，惊讶于WCF4已经能如此完美实现REST（好吧，我承认不是完全完美）。期间遇到了许多难题，但还是一一解决，或者找到了workaround. 为了让以后想做同样事情的朋友不至于像我们这般抓狂，也为了给自己整理一下所学，我准备写一个系列，讲述如何从零开始创建一个完整的支持Token验证的REST服务，其内容将涉及如何控制返回内容格式，如何处理异常并返回自定义内容，如何建立基于token的身份验证，如何使用POCO，如何做service的单元测试……总之，总之，看了就知道了。鉴于文笔拙劣，技亦不精，不敢和大牛比肩，姑且自称为《我的WCF4 Rest Service及Entity Framework with POCO之旅》吧，还请各位高手多多指教。</summary><published>2011-03-28T01:11:00Z</published><updated>2011-03-28T01:11:00Z</updated><author><name>Gildor Wang</name><uri>http://www.cnblogs.com/Gildor/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Gildor/archive/2011/03/28/1997426.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Gildor/archive/2011/03/28/1997426.html"/><content type="html">&lt;p&gt;&lt;font size="2"&gt;很久没有玩WCF了，年前和几个朋友做一个Restful的服务，惊讶于WCF4已经能如此完美实现REST（好吧，我承认不是完全完美）。期间遇到了许多难题，但还是一一解决，或者找到了workaround. 为了让以后想做同样事情的朋友不至于像我们这般抓狂，也为了给自己整理一下所学，我准备写一个系列，讲述如何从零开始创建一个完整的支持Token验证的REST服务，其内容将涉及如何控制返回内容格式，如何处理异常并返回自定义内容，如何建立基于token的身份验证，如何使用POCO，如何做service的单元测试……总之，总之，看了就知道了。鉴于文笔拙劣，技亦不精，不敢和大牛比肩，姑且自称为《我的WCF4 Rest Service及Entity Framework with POCO之旅》吧，还请各位高手多多指教。&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;照理说，首先应介绍什么是&lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer" target="_blank"&gt;REST&lt;/a&gt;，它是怎么出现的，有什么优势，什么历史意义。不过我不多想讲，也讲不清楚。简而言之，REST最核心的概念是“资源”，一个uri代表一个特定的资源，而客户端通过HTTP method GET, POST, PUT, DELETE来和资源交互（同时导致状态转移）。至于资源的表现形式，则以XML和JSON为主。为什么要用REST? 自己想去吧。&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;创建项目&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;闲话说完，现在开始创建项目。使用Online Templates中的WCF REST Service Template是一个比较容易的方法：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911008926.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911022249.png" width="644" height="446" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;项目创建好后，我们可以看到项目中已经有下面这些文件：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/20110328091103232.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911032674.png" width="261" height="194" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Service1就是一个WCF Service，和以前的WCF Service不同，这里的Service既不是一个.svc文件，也没有专门定义一个接口作为ServiceContract。&lt;/p&gt;  &lt;p&gt;SampleItem就是我们以往所知的DataContract，不过现在即使不加DataContract attribute，也没有关系。&lt;/p&gt;  &lt;p&gt;比较特别的是Global.asax和Global.asax.cs。Global.asax.cs中定义了Routes：&lt;/p&gt;  private void RegisterRoutes()&lt;br/&gt;{&lt;br/&gt;    // Edit the base address of Service1 by replacing the &amp;quot;Service1&amp;quot; string below&lt;br/&gt;    RouteTable.Routes.Add(new ServiceRoute(&amp;quot;Service1&amp;quot;, new WebServiceHostFactory(), typeof(Service1)));&lt;br/&gt;}&lt;p&gt;表示所有访问&lt;a href="http://xxx/Service1"&gt;HOST/Service1&lt;/a&gt;的请求都要由Service1这个类来处理。再看一下Service1的定义，其中有个方法是：&lt;/p&gt;[WebGet(UriTemplate = &amp;quot;{id}&amp;quot;)]&lt;br/&gt;public SampleItem Get(string id)&lt;br/&gt;{&lt;br/&gt;    // TODO: Return the instance of SampleItem with the given id&lt;br/&gt;    throw new NotImplementedException();&lt;br/&gt;}&lt;p&gt;这表示对于HOST/Service1/123的GET请求，将会用这个Get(string)方法来处理。服务运行时，WCF根据uri中的“Service1”根据前面指定的Route找到Service1这个服务类，再根据UriTemplate，将“123”赋给名为id的参数，其结果就是Get方法以参数“123”被调用了。&lt;/p&gt;&lt;p&gt;现在我们修改一下这个方法，让它能够返回结果：&lt;/p&gt;[WebGet(UriTemplate = &amp;quot;{id}&amp;quot;)]&lt;br/&gt;public SampleItem Get(string id)&lt;br/&gt;{&lt;br/&gt;    return new SampleItem&lt;br/&gt;    {&lt;br/&gt;        Id = int.Parse(id), &lt;br/&gt;        StringValue = string.Format(&amp;quot;The id is {0}.&amp;quot;, id) &lt;br/&gt;    };&lt;br/&gt;}&lt;p&gt;然后启动这个服务。在浏览器地址栏中输入“&lt;a href="http://localhost:6421/Service1/123"&gt;http://localhost:6421/Service1/123&lt;/a&gt;”（端口号根据实际情况而定），结果如下：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911045672.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911047591.png" width="395" height="100" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;如何，是不是很简单？&lt;/p&gt;&lt;p&gt;不过现在的RESTful服务还只能读取资源，WebGet只支持GET方法，要支持PUT, POST和DELETE，需要使用WebInvoke attribute。而且，现在的服务看不出任何实际意义。&lt;/p&gt;&lt;p&gt;既然掌握了基本技术，我们来创建一个稍微有点实际意义的服务&lt;/p&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;创建一个简易微博服务&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;（终于不是做图书管理系统示例了）&lt;/p&gt;&lt;p&gt;首先，我们的微博服务真的很简单，简单到只能有一个用户自娱自乐，而且还只能发文字，是不是有点无聊？不过不要急，一步一步来。&lt;/p&gt;&lt;p&gt;既然如此，所谓的微博服务，也就是一个微博管理系统了(…)。它应该有这些功能：&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;发布微博 &lt;/li&gt;  &lt;li&gt;查看已发布的微博 &lt;/li&gt;  &lt;li&gt;删除一条微博 &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;照此删去原先那些示例代码，重新编写代码如下：&lt;/p&gt;&lt;hr /&gt;MicroblogService.cs using System;&lt;br/&gt;using System.Collections.Concurrent;&lt;br/&gt;using System.Collections.Generic;&lt;br/&gt;using System.Linq;&lt;br/&gt;using System.ServiceModel;&lt;br/&gt;using System.ServiceModel.Activation;&lt;br/&gt;using System.ServiceModel.Web;&lt;br/&gt;using System.Threading;&lt;br/&gt;namespace WcfRestServiceDemo.Service&lt;br/&gt;{&lt;br/&gt;    [ServiceContract(Namespace = &amp;quot;WcfRestServiceDemo&amp;quot;)]&lt;br/&gt;    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] &lt;br/&gt;    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] &lt;br/&gt;    public class MicroblogService &lt;br/&gt;    {&lt;br/&gt;        private static int _currentId;  &lt;br/&gt;        private static readonly ConcurrentDictionary&amp;lt;int, Microblog&amp;gt; _microblogs =  &lt;br/&gt;            new ConcurrentDictionary&amp;lt;int, Microblog&amp;gt;(); &lt;br/&gt;&lt;br/&gt;        [WebGet(UriTemplate = &amp;quot;&amp;quot;)]  &lt;br/&gt;        public List&amp;lt;Microblog&amp;gt; GetCollection()  &lt;br/&gt;        {  &lt;br/&gt;            return _microblogs.Values.ToList();  &lt;br/&gt;        }  &lt;br/&gt;&lt;br/&gt;        [WebInvoke(UriTemplate = &amp;quot;&amp;quot;, Method = &amp;quot;POST&amp;quot;)]  &lt;br/&gt;        public Microblog Create(Microblog microblog)  &lt;br/&gt;        {  &lt;br/&gt;            microblog.Id = Interlocked.Increment(ref _currentId);  &lt;br/&gt;            microblog.PublishTime = DateTime.Now;  &lt;br/&gt;            _microblogs.TryAdd(microblog.Id, microblog);  &lt;br/&gt;            return microblog;  &lt;br/&gt;        }  &lt;br/&gt;&lt;br/&gt;        [WebGet(UriTemplate = &amp;quot;{id}&amp;quot;)]  &lt;br/&gt;        public Microblog Get(string id)  &lt;br/&gt;        {  &lt;br/&gt;            return _microblogs[int.Parse(id)];  &lt;br/&gt;        }  &lt;br/&gt;&lt;br/&gt;        [WebInvoke(UriTemplate = &amp;quot;{id}&amp;quot;, Method = &amp;quot;DELETE&amp;quot;)]  &lt;br/&gt;        public void Delete(string id)  &lt;br/&gt;        {  &lt;br/&gt;            Microblog microblog;  &lt;br/&gt;            _microblogs.TryRemove(int.Parse(id), out microblog);  &lt;br/&gt;        }  &lt;br/&gt;    }&lt;br/&gt;} &lt;font face="Comic Sans MS"&gt;&lt;font style="font-size: 12pt"&gt;&lt;/font&gt;&lt;/font&gt;&lt;hr /&gt;Microblog.cs using System;&lt;br/&gt;using System.Runtime.Serialization;&lt;br/&gt;namespace WcfRestServiceDemo.Service&lt;br/&gt;{&lt;br/&gt;    [DataContract(Namespace = &amp;quot;WcfRestServiceDemo&amp;quot;)]&lt;br/&gt;    public class Microblog&lt;br/&gt;    {&lt;br/&gt;        [DataMember]&lt;br/&gt;        public int Id { get; set; }&lt;br/&gt;&lt;br/&gt;        [DataMember]&lt;br/&gt;        public string Content { get; set; }&lt;br/&gt;&lt;br/&gt;        [DataMember]&lt;br/&gt;        public DateTime PublishTime { get; set; }&lt;br/&gt;    }&lt;br/&gt;}&lt;font face="Comic Sans MS"&gt;&lt;font style="font-size: 12pt"&gt;&lt;/font&gt;&lt;/font&gt;&lt;hr /&gt;Global.asax.cs using System;&lt;br/&gt;using System.ServiceModel.Activation;&lt;br/&gt;using System.Web;&lt;br/&gt;using System.Web.Routing;&lt;br/&gt;namespace WcfRestServiceDemo.Service&lt;br/&gt;{&lt;br/&gt;    public class Global : HttpApplication&lt;br/&gt;    {&lt;br/&gt;        private void Application_Start(object sender, EventArgs e)&lt;br/&gt;        {&lt;br/&gt;            RegisterRoutes();&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        private void RegisterRoutes()&lt;br/&gt;        {&lt;br/&gt;            RouteTable.Routes.Add(new ServiceRoute(&amp;quot;microblogs&amp;quot;, new WebServiceHostFactory(), typeof(MicroblogService)));&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;p&gt;编译并运行。首先访问&lt;a href="http://localhost:6421/microblogs/@all"&gt;http://localhost:6421/microblogs/@all&lt;/a&gt;：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911043970.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911053381.png" width="187" height="54" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;理所当然什么都没有，一条微博都还没发呢。为了方便的发送请求并查看结果，祭出神器Fiddler2:   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911065408.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911087162.png" width="644" height="364" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;在右边窗口可以看到请求返回的XML。&lt;/p&gt;&lt;p&gt;接下来选择右边上面的Request Builder，来构造一个POST:   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911097619.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911117388.png" width="626" height="469" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;（可以把左边那个请求拖入编辑窗口快速复制一个请求。）   &lt;br /&gt;（注意要在上面的Headers窗口添加Content-Type: application/xml这一项，同时注意XML的namespace。）&lt;/p&gt;&lt;p&gt;点击Execute：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911139698.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911153404.png" width="644" height="364" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;服务返回HTTP 200 OK，并且可以从Response的内容中看到成功创建的Microblog的Id以及PublishTime的值。&lt;/p&gt;&lt;p&gt;再次获取全部：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911169651.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911184436.png" width="644" height="364" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;已经可以看到刚才创建的微博了。现在再试一下其他两个功能：&lt;/p&gt;&lt;p&gt;获取单条微博：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911208939.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911222088.png" width="644" height="364" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;删除指定微博：   &lt;br /&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911246350.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/Gildor/201103/201103280911267199.png" width="644" height="364" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;再次查询全部，结果又是空空如也了。到这里，一个超级简单的“微博”服务已经搭建起来了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;小结&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;本文从零开始，依次介绍了&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;什么是REST &lt;/li&gt;  &lt;li&gt;如何创建一个基本的WCF REST Service &lt;/li&gt;  &lt;li&gt;（如何使用Fiddler2……） &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;em&gt;To be continued…&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;下篇预告：我的WCF4 Rest Service及Entity Framework with POCO之旅（二）——选择请求/返回格式&lt;/em&gt;&lt;/p&gt;&lt;em&gt;  &lt;hr /&gt;参考：&lt;/em&gt; &lt;ul&gt;  &lt;li&gt;&lt;em&gt;一些基础知识： &lt;/em&gt;&lt;a href="http://msdn.microsoft.com/en-us/magazine/dd315413.aspx"&gt;&lt;em&gt;http://msdn.microsoft.com/en-us/magazine/dd315413.aspx&lt;/em&gt;&lt;/a&gt; &lt;/li&gt;&lt;/ul&gt;&lt;hr /&gt;&lt;strong&gt;我的WCF4 REST Service及Entity Framework with POCO之旅系列&lt;/strong&gt; &lt;ul&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/28/1997426.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（一）——创建一个基本的RESTful Service&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/29/1998392.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（二）——选择请求/返回格式&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/31/2000553.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（三）——用Entity Framework和POCO Template实现数据模型及存储&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/04/20/2022013.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（四）——定制Entity&lt;/a&gt; &lt;/li&gt;  &lt;li&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/08/22/2148804.html"&gt;我的WCF4 REST Service及Entity Framework with POCO之旅（五）——身份验证&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://www.cnblogs.com/Gildor/aggbug/1997426.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/03/28/1997426.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Gildor/archive/2011/01/18/1937886.html</id><title type="text">再说说C#定义事件的写法</title><summary type="text">C#定义事件的“标准”写法，在很多地方都可以看到，其最主要的功能是防止调用订阅者列表为空的委托。但是其中有两处让人第一眼看上去感到疑惑的地方，至少我不是一开始就理解的。</summary><published>2011-01-17T22:05:00Z</published><updated>2011-01-17T22:05:00Z</updated><author><name>Gildor Wang</name><uri>http://www.cnblogs.com/Gildor/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Gildor/archive/2011/01/18/1937886.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Gildor/archive/2011/01/18/1937886.html"/><content type="html">&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:3fc09bca-b336-4daa-9be7-29ed704bbf62" class="wlWriterEditableSmartContent"&gt;&lt;div&gt;&lt;!--&lt;br/&gt;&lt;br/&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/&gt;http://www.CodeHighlighter.com/&lt;br/&gt;&lt;br/&gt;--&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;event&lt;/span&gt;&lt;span style="color: #000000;"&gt; EventHandler Started;&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;protected&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;virtual&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; OnStarted(EventArgs e)&lt;br/&gt;{&lt;br/&gt;    EventHandler handler &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; Started;&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;if&lt;/span&gt;&lt;span style="color: #000000;"&gt; (handler &lt;/span&gt;&lt;span style="color: #000000;"&gt;!=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;)&lt;br/&gt;    {&lt;br/&gt;        handler(&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;, e);&lt;br/&gt;    }&lt;br/&gt;}&lt;/span&gt;&lt;/div&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;&lt;p&gt;像上面这段代码，在很多地方都可以看到，几乎可说是创建事件的标准写法了。这段代码最主要的功能是防止调用订阅者列表为空的委托。但是其中有两处让人第一眼看上去感到疑惑的地方，至少我不是一开始就理解的。&lt;/p&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;首先是显眼的protected virtual. 相信搞过WinForm编程的都很熟悉，因为从WinForm控件继承后可以看到大量的如OnClick(EventArgs), OnKeyDown(KeyEventArgs)这样的保护虚方法。用Reflector看System.Windows.Forms.Control，可以发现OnClick(EventArgs)方法定义如下：&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:e7846649-a320-4ed0-995d-8cd3824df985" class="wlWriterEditableSmartContent"&gt;&lt;div&gt;&lt;!--&lt;br/&gt;&lt;br/&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/&gt;http://www.CodeHighlighter.com/&lt;br/&gt;&lt;br/&gt;--&gt;&lt;span style="color: #000000;"&gt;[EditorBrowsable(EditorBrowsableState.Advanced)]&lt;br/&gt;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;protected&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;virtual&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; OnClick(EventArgs e)&lt;br/&gt;{&lt;br/&gt;    EventHandler handler &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; (EventHandler) &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;base&lt;/span&gt;&lt;span style="color: #000000;"&gt;.Events[EventClick];&lt;br/&gt;    &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;if&lt;/span&gt;&lt;span style="color: #000000;"&gt; (handler &lt;/span&gt;&lt;span style="color: #000000;"&gt;!=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;)&lt;br/&gt;    {&lt;br/&gt;        handler(&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;this&lt;/span&gt;&lt;span style="color: #000000;"&gt;, e);&lt;br/&gt;    }&lt;br/&gt;}&lt;/span&gt;&lt;/div&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;&lt;p&gt;这和前面看到的定义是一致的，可见.NET中正是遵循了这个写法。从这个方法在WinForm中的应用，也就能看出这样写的意义了。声明为protected可以让派生类自由触发事件，而virtual则令派生类可以在改变事件触发时的行为，比如在事件触发前后进行一些操作，或者决定是否要触发事件。&lt;/p&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;其次是handler的定义，看起来handler是一个冗余变量，完全多此一举。而且违反了重构中的“尽量不使用局部变量”的原则。我一开始甚至做过手动inline这样的代码中的handler。然而，事实上在多线程环境中这一行代码是必不可少的，因为可能在handler != null 判断通过后，Started在另一个线程中被设为了null! 因此，将Started首先赋给一个局部变量，避免了外界环境的影响。&lt;/p&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;但是还没有完，上面说的都是理论，如果我说，这样的代码明显是会被编译器优化掉的，因此这样写完全没有意义，怎样呢？毕竟EventHandler作为一个委托，并没有用volatile关键字声明（事实上事件不能声明为volatile，但可以在这里用Thread.VolatileRead(ref object)方法），使用时也没有用Interlocked来访问。我其实真没有想到那么远，不过CLR Via C#上给出了解释（记不得是哪一章了）：JIT的编译器在这里会识别出这个写法并且确保不会把handler变量优化掉。真是万幸，但估计又成为了一个被学院派的诟病的特性。不过管他呢，让我们少写一些代码难道不是好事么，至少我觉得很好。&lt;/p&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;另外值得一提的是，如果只是很简单的类，并且预料到不会有什么继承形式（最好同时标记为sealed），而不想写出那一坨代码的话（tip: 输invoke插入snipplet可以稍微减轻一点负担），可以采用下面一种写法偷偷懒：&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:9e22db48-f685-46ad-b538-0d82d3de7c9e" class="wlWriterEditableSmartContent"&gt;&lt;div&gt;&lt;!--&lt;br/&gt;&lt;br/&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br/&gt;http://www.CodeHighlighter.com/&lt;br/&gt;&lt;br/&gt;--&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;event&lt;/span&gt;&lt;span style="color: #000000;"&gt; EventHandler Started &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;delegate&lt;/span&gt;&lt;span style="color: #000000;"&gt; { };&lt;/span&gt;&lt;/div&gt;&lt;!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/Gildor/aggbug/1937886.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/01/18/1937886.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Gildor/archive/2011/01/14/1935152.html</id><title type="text">对需要读取config文件的程序集进行单元测试</title><summary type="text">其实只要在Tests项目中加入app.config就可以了，不过这往往意味着要同时维护两份config文件。有两种更加优雅的办法： 使用Add Existing File, 然后选择add as li...</summary><published>2011-01-13T19:39:00Z</published><updated>2011-01-13T19:39:00Z</updated><author><name>Gildor Wang</name><uri>http://www.cnblogs.com/Gildor/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Gildor/archive/2011/01/14/1935152.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Gildor/archive/2011/01/14/1935152.html"/><content type="html">&lt;p&gt;其实只要在Tests项目中加入app.config就可以了，不过这往往意味着要同时维护两份config文件。有两种更加优雅的办法：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;使用Add Existing File, 然后选择add as link。但是貌似对于网站类型的项目就没有办法了，因为原文件名是web.config，而测试项目中必须是app.config才行，而add as link又没办法重命名。&lt;/li&gt;    &lt;li&gt;通过配置.testsettings, 使用Test Deployment来实现自动拷贝文件。具体可以参考&lt;a href="http://stackoverflow.com/questions/344069/can-a-unit-test-project-load-the-target-applications-app-config-file" target="_blank"&gt;这里&lt;/a&gt;。&lt;/li&gt; &lt;/ul&gt;&lt;img src="http://www.cnblogs.com/Gildor/aggbug/1935152.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/01/14/1935152.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Gildor/archive/2011/01/14/1935149.html</id><title type="text">使用Entity Framework遇到MetadataException: Unable to load the specified metadata resource</title><summary type="text">项目中使用了Entity Framework, 然后在一个单元测试项目中引用包含了edmx的项目，跑测试时，遇到了MetadataException: Unable to load the speci...</summary><published>2011-01-13T18:44:00Z</published><updated>2011-01-13T18:44:00Z</updated><author><name>Gildor Wang</name><uri>http://www.cnblogs.com/Gildor/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Gildor/archive/2011/01/14/1935149.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Gildor/archive/2011/01/14/1935149.html"/><content type="html">&lt;p&gt;项目中使用了Entity Framework, 然后在一个单元测试项目中引用包含了edmx的项目，跑测试时，遇到了MetadataException: Unable to load the specified metadata resource。&lt;/p&gt;  &lt;p&gt;Google后找到了解决办法，在Entity Framework生成的连接字符串中，包含了&lt;strong&gt;&amp;quot;metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;&amp;quot;&lt;/strong&gt;这一段。其中的*表示将在所有程序集中查找Model.csdl, Model.ssdl, Model.msl这3个资源。然而在有些情况下似乎并不能正确的找到，而将其改为显示指定就可以解决：&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;metadata=res://Something.Data/Model.csdl|res://Something.Data/Model.ssdl|res://Something.Data/Model.msl;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.teamb.com/craigstuntz/2010/08/13/38628/trackback/" target="_blank"&gt;这里&lt;/a&gt;一篇博客介绍了更详细的信息。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/Gildor/aggbug/1935149.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Gildor/archive/2011/01/14/1935149.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
