使用StructureMap扩展ASP.NET MVC三层架构1-Entity Framework Model层的实现

今天开始天屹将给大家分享一个使用StructureMap扩展ASP.NET MVC三层架构,并使用StructureMap实现依赖注入,充分的使代码之间的耦合度降到最低。由于时间精力有限天屹无法在一篇文章中全部介绍完,将把文章写成一个系列文章。系统将使用最新的MVC4和EntityFramework5.0。众所周知MVC4新建的默认工程很简单,而且Model层是和Views,Controller在一个Project里面的,这样的结构很不美观也很不利于维护,最重要的一点没有Service层和Repository层。

本篇文章将向大家介绍如何添加Service和Repository层并且使用StructureMap把Service层注入到Controller,把Repository注入到Service层。Service层主要是我们的业务逻辑层,这一层不和底层的Database打交道,和Database打交道的是Repository数据持久层。本篇文章通过使用StructureMap依赖注入使Controller,Service,Repository三层的耦合度降到最低。

本系统使用NorthWind开源数据,并且使用EntityFramework5.0实现对数据库的Object映射。

开始正题之前先来看一下成型的框架结构,我们将围绕这个截图进行展开。

mvc-customized-framework

首先我们看TYStudioDemo.Models这个Project里面的内容

mvc-customized-framework

这里面有我们的EntityFramwork的edmx文件,Northwind的数据库表映射的对象集合。这里建立ADO.Net Entity Data Model的时候没有使用默认生成一堆.tt文件的方式,而是使用了老的形式。实现方法是首先按默认程序建立起data model,建立好data model之后删除.tt文件。然后打开.edmx文件,右键单击空白处选择Properties(属性),会出现下面的截图,这时候只需要修改一下Code Generation Strategy(中文翻译:代码生成策略,感谢独孤青同学提供的中文翻译)的值,默认是None,我们修改为Default,然后保存.edmx,ok,万事大吉。

mvc-customized-framework

你应该已经注意到了,项目里多了一个TYEntities.cs文件,这个我们是我们这个系统中实现Transaction(事务处理)的关键。我们使用static和[ThreadStatic]属性来保证一个线程拿到的TYEntities(ObjectContext)总是同一个,这就解决了Transaction事务的问题。没有解释到的请详细阅读下面代码里面的注释。

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Web;
  7.  
  8. namespace TYStudioDemo.Models
  9. {
  10.     public partial class TYEntities
  11.     {
  12.         #region Fields
  13.  
  14.         //定义索引名称
  15.         const string ContextKey = "TYEntities";
  16.  
  17.         //标记为ThreadStaticAttribute的静态字段不在线程之间共享。
  18.         //每个执行线程都有单独的字段实例,并且独立地设置及获取该字段的值。如果在不同的线程中访问该字段,则该字段将包含不同的值。
  19.         [ThreadStatic]
  20.         private static TYEntities _current;
  21.  
  22.         #endregion
  23.  
  24.         #region Properties
  25.  
  26.         public bool Disposed { get; set; }
  27.  
  28.         /// <summary>
  29.         /// 当系统工作在HttpContext下,将使用延迟家在技术返回一个TYEntities(ObjectContext),如果没有HttpContext将返回null
  30.         ///
  31.         /// 不论在哪里使用TYEntities,在请求结束后都需要调用TYEntities.Cleanup()方法
  32.         /// 最佳的方式是TYEntities.Cleanup()放到Global.asax.cs文件里面。
  33.         /// void Application_EndRequest(object sender, EventArgs e)
  34.         /// {
  35.         ///     TYStudioDemo.Models.TYEntities.Cleanup();
  36.         /// }
  37.         /// </summary>
  38.         private static TYEntities ForWebRequest
  39.         {
  40.             get
  41.             {
  42.                 var context = HttpContext.Current;
  43.  
  44.                 //检查HttpContext是否存在
  45.                 if (context != null)
  46.                 {
  47.                     //试着从context中得到TYEntities
  48.                     var result = context.Items[ContextKey] as TYEntities;
  49.  
  50.                     if (result == null)
  51.                     {
  52.                         //创建新的datacontext,并且保存到context里面
  53.                         result = new TYEntities();
  54.                         context.Items[ContextKey] = result;
  55.                     }
  56.  
  57.                     return result;
  58.                 }
  59.  
  60.                 return null;
  61.             }
  62.         }
  63.  
  64.         /// <summary>
  65.         /// 这是一个用来获取TYEntities(ObjectContext)的公共属性
  66.         ///
  67.         /// 如果你通过HttpContext获取TYEntities,同样不论在哪里使用TYEntities,在请求结束后都需要调用TYEntities.Cleanup()方法
  68.         ///
  69.         /// 如果没有通过HttpContext获取TYEntities,你必须在使用结束之后调用TYEntities.Cleanup()方法,来清理ObjectContext。
  70.         ///
  71.         /// 需要注意的一点是,无论使用哪种方式获取TYEntities,我们都必须手动的清理和Dispose TYEntities(ObjectContext)。
  72.         /// 所以一定不要在using块中使用TYEntities(ObjectContext)。
  73.         /// </summary>
  74.         public static TYEntities Current
  75.         {
  76.             get
  77.             {
  78.                 //从HttpContext中获取datacontext
  79.                 var result = TYEntities.ForWebRequest;
  80.  
  81.                 if (result != null)
  82.                     return result;
  83.  
  84.                 //试着获取当前活动的TYEntities
  85.                 if (_current == null)
  86.                     _current = new TYEntities();
  87.  
  88.                 return _current;
  89.             }
  90.         }
  91.  
  92.         /// <summary>
  93.         /// 清理结束TYEntities(ObjectContext)
  94.         /// </summary>
  95.         public static void Cleanup()
  96.         {
  97.             if (HttpContext.Current != null)
  98.             {
  99.                 var result = HttpContext.Current.Items[ContextKey] as TYEntities;
  100.  
  101.                 if (result != null)
  102.                     result.Dispose();
  103.  
  104.                 HttpContext.Current.Items[ContextKey] = null;
  105.             }
  106.             else if (_current != null)
  107.             {
  108.                 _current.Dispose();
  109.                 _current = null;
  110.             }
  111.         }
  112.  
  113.         protected override void Dispose(bool disposing)
  114.         {
  115.             bool disposed = Disposed;
  116.             Disposed = true;
  117.  
  118.             if (!disposed)
  119.                 Cleanup();
  120.  
  121.             base.Dispose(disposing);
  122.         }
  123.  
  124.         #endregion
  125.     }
  126. }

由于时间问题,今天就先介绍TYStudio.Models的实现。在以后的文章中会陆续介绍各个Project的内容,所有Project介绍完之后,会在工程中加入日志和异常的处理,我们使用EnterPrise Library来实现他们。同时也会把天屹前段时间写的MVC4 Simplemembership权限管理的系统集成进来。

天屹一定抽出时间会尽快更新完成这一系列的文章,全部文章结束之后天屹会公开分享系统的代码供大家下载。



10 评论

  1. titttt   •  

    class TYEntities : NorthwindEntities

    这个继承关系都没加。你代码怎么运行的

    • 天屹   •     作者

      谢谢指出截图错误,你好细心啊,这个是截图的问题,已更新图片。开始我使用的是NorthwindEntities这个命名,后来改成了TYEntities,可能是中间的时候截的图,截出来的就是你看的效果了。所以代码运行是没问题的。

  2. 完美   •  

    我在按默认程序建立起data model,建立好data model之后删除.tt文件。然后打开.edmx文件,修改一下Code Generation Strategy,再保存,并没有在项目里多了一个TYEntities.cs文件。我用VS2012。
    这个文件哪里来的?

    • 天屹   •     作者

      确实没有多出来TYEntities.cs这个文件,如果你简历EntityFramwork的时候名字写的是TYEntities,生成designer文件里会以TYEntities命名为整个objectcontext, 项目中的TYEntities.cs这个文件时对原有的TYEntities的扩展,是partial的.还不清楚加我qq说也可以.

  3. 你的小伙伴   •  

    群主威武,群主荡漾,群主求解,ViewModel是不是忒麻烦了点,还有什么好的解决思路吗。

    • 天屹   •     作者

      麻烦是麻烦了点,但是扩展性和定制性有了很好扩充,好处会后再后期维护的时候提现,如果不想用viewmdel也是可以的,ViewModel的好处体现在强类型View中,使用弱类型的话可以不使用ViewModel自定义这种方式,目前我是没有什么好的解决思路。但是你可以自己开发一个自动生成ViewModel的工具,这也是一个节省时间的方法。

  4. 独孤青   •  

    @天屹,Code Generation Strategy 的中文翻译为: 代码生成策略

    • 天屹   •     作者

      非常谢谢,已更新到文章。一直用英文的开发环境,好多地方都不知道中文版是怎么翻译的~

  5. helloaccp   •  

    请问一下viewmodel 定义的属性与model中的是一样的吗?如果不一样有啥区别呢

    • 天屹   •     作者

      可以一样可以不一样。 一样的话,你可以额外添加一些验证信息,或者验证的条件。 不一样的话你可以自己定义字段的类型和关系类的字段。 就是增加了扩展性。

天屹进行回复 取消回复

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>