[.NET领域驱动设计实战系列]专题一:前期准备之EF CodeFirst

出处:http://www.cnblogs.com/zhili/p/EFCodeFirst.html


一、前言

  从去年已经接触领域驱动设计(Domain-Driven Design)了,当时就想自己搭建一个DDD框架,所以当时看了很多DDD方面的书,例如领域驱动模式与实战,领域驱动设计:软件核心复杂性应对之道和领域驱动设计C# 2008实现等书,由于当时只是看看而已,并没有在自己代码中进行实现,只是初步了解一些DDD分层的思想和一些基本概念,例如实体,聚合根、仓储等概念,今年有机会可以去试试面试一个架构岗位的时候,深受打击,当面试官问起是否在项目中使用过DDD思想来架构项目时,我说没有,只是了解它的一些基本概念。回来之后,便重新开始学习DDD,因为我发现做成功面试一个架构师,则必须有自己的一个框架来代表自己的知识体系,而不是要你明白这个基本概念。此时学习便决定一步步来搭建一个DDD框架。但是这次的过程并不是像dax.net那样,一开始就去搭建框架,然后再用一个实际的项目来做演示框架的使用。因为我觉得这样对于一些初学者来学习的话,难度比较大,因为刚开始写框架根本看到什么,而且看dax.net的Apworks框架很多代码也不明白他为什么这么写的,从框架代码并不能看出作者怎么一步步搭建框架的,读者只能一下子看到整个成型的框架,对于刚接触DDD的朋友难度非常大,以至于学习了一段时间的DDD之后,就放弃了。这个感觉本人学习过程中深有体会。所以本系列将直接把DDD的思想应用到一个实例项目中,完全实例项目后,再从中抽取一个DDD框架出来,并且会一步步介绍如何将DDD的思想应用到一个实际项目中(dax.net中ByteartRetail项目也是直接给出一个完整的DDD演示项目的,并没有记录搭建过程,同样对于读者学习难度很大,因为一下子来吸收整个项目的知识,接受不了,读者自然就心灰意冷,也就没有继续学习DDD的动力了)。本文并没有开始介绍DDD项目的实际实现,而是一个前期准备工作,因为DDD项目中一般会使用的实体框架来完成,作为.NET阵营的人,自然首先会使EntityFramework。下面就具体介绍下EF中code First的实现,因为在后面的DDD项目实现中会使用到EF的CodeFirst。

二、EF CodeFirst的实现步骤

  因为我之前没怎么接触EF的CodeFirst实现,所以在看dax.net的ByteartRetail项目的时候,对EF仓储的实现有疑惑,所以去查阅相关EF的教程发现,原来应用了EF中的CodeFirst。所以把过程记录下来。下面就具体介绍下使用EF CodeFirst的具体实现步骤。

  步骤一:创建一个Asp.net MVC 4 Web项目,创建成功后,再添加一个Model类

  CodeFirst自然是先写实体类了,这里演示的是一个Book实体类,具体类的实现代码如下:

复制代码

publicclassBook
{publicintBookId{get;set;}publicstringBookName{get;set;}publicstringAuthor{get;set;}publicstringPublisher{get;set;}publicdecimalPrice{get;set;}publicstringRemark{get;set;}
}

复制代码

  将使用这个类表示数据库中的一个表,每个Book类的实例对应数据库中的一行,Book类中的每个属性映射为数据库中的一列。

  步骤二:创建“BookDbContext”的类

  使用Nuget安装Entity Framework,安装成功后,在Models文件夹下新建一个“BookDbContext”的类,将类派生自“DbContext”类(命名空间为System.Data.Entity,dll在EntityFramework),具体BookDbContext的实现如下:

usingSystem.Data.Entity;publicclassBookDbContext:DbContext
{publicDbSet<Book>Books{get;set;}
}

  BookDbContext代表EF中Book在数据库中的上下文对象,通过DbSet<Book>使实体类与数据库关联起来。Books属性表示数据中的数据集实体,用来处理数据的存取与更新。

  步骤三:添加数据库连接

  在Web.config文件中,修改数据库连接字符串的配置,这里将数据库连接的name属性设置为BookDbContext,后面代码将会使用到该名字,并根据连接创建相应的数据库。

<connectionStrings>
<addname="BookDbContext"connectionString="DataSource=(LocalDb)\v11.0;InitialCatalog=BookDB;IntegratedSecurity=True;AttachDBFilename=|DataDirectory|\BookDB.mdf"providerName="System.Data.SqlClient"/>
</connectionStrings>

  步骤四:为Book创建控制器和Index视图

  首先创建一个控制器:在"Controlllers"上右键>添加>控制器,在打开的添加控制器对话框中,将控制器的名称改为"BookController",模板选择”空控制器“。修改BookController的代码为如下所示:

复制代码

publicclassBookController:Controller
{readonlyBookDbContext_db=newBookDbContext();//
//GET:/Book/

publicActionResultIndex()
{varbooks=frombin_db.Bookswhereb.Author=="Learninghard"
selectb;returnView(books.ToList());
}publicActionResultCreate()
{returnView();
}
}

  在Index方法内右键>"添加视图",在打开的”添加视图“对话框,勾选”创建强类型视图“,在模型类列表中选择”Book“(如果选择列表为空,则需要首先编译下项目),在支架模板列表中选择”List“,具体如下图所示:

  点击添加按钮,VS为我们创建了Index.cshtml文件,修改Index.cshtml代码为如下所示的代码:

复制代码

@modelIEnumerable<EF_CodeFirstImp.Models.Book>@{
ViewBag.Title="图书列表-EFCodeFirstImp";
}<h2>Index</h2><p>
@Html.ActionLink("添加图书","Create")</p><table>
<tr>
<th>
图书名称</th>
<th>
作者</th>
<th>
出版社</th>
<th>
价格</th>
<th>
备注</th>
<th></th>
</tr>@foreach(variteminModel){<tr>
<td>
@Html.DisplayFor(modelItem=>item.BookName)</td>
<td>
@Html.DisplayFor(modelItem=>item.Author)</td>
<td>
@Html.DisplayFor(modelItem=>item.Publisher)</td>
<td>
@Html.DisplayFor(modelItem=>item.Price)</td>
<td>
@Html.DisplayFor(modelItem=>item.Remark)</td>
<td>
@Html.ActionLink("编辑","Edit",new{id=item.BookId})|
@Html.ActionLink("查看","Details",new{id=item.BookId})|
@Html.ActionLink("删除","Delete",new{id=item.BookId})</td>
</tr>}</table>

  编译并运行程序,在浏览器中输入地址:http://localhost:2574/Book,得到的运行结果如下:

  尽管没有数据,但EF已经为我们创建了相应的数据库了。此时在App_Data文件夹下生成了BookDB数据库,在解决方案点击选择所有文件,将BookDB数据库包括在项目中。

  步骤五:添加Create视图

  在BookController中的Create方法右键添加视图来添加Create视图,此时模型类仍然选择Book,但支架模板选择"Create"。添加成功后,VS会在Views/Book目录下添加一个Create.cshtml文件,由于这里选择了Create支架框架,所以VS会为我们生成一些默认的代码。在这个视图模板中,指定了强类型Book作为它的模型类,VS检查Book类,并根据Book类的属性,生成对应的标签名和编辑框,我们修改标签使其显示中文,修改会的代码如下所示:

复制代码

@modelEF_CodeFirstImp.Models.Book

@{
ViewBag.Title="添加图书";
}<h2>添加图书</h2>@using(Html.BeginForm()){
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)<fieldset>
<legend>图书</legend>

<divclass="editor-label">
图书名称:</div>
<divclass="editor-field">
@Html.EditorFor(model=>model.BookName)
@Html.ValidationMessageFor(model=>model.BookName)</div>

<divclass="editor-label">
作者:</div>
<divclass="editor-field">
@Html.EditorFor(model=>model.Author)
@Html.ValidationMessageFor(model=>model.Author)</div>

<divclass="editor-label">
出版社:</div>
<divclass="editor-field">
@Html.EditorFor(model=>model.Publisher)
@Html.ValidationMessageFor(model=>model.Publisher)</div>

<divclass="editor-label">
价格:</div>
<divclass="editor-field">
@Html.EditorFor(model=>model.Price)
@Html.ValidationMessageFor(model=>model.Price)</div>

<divclass="editor-label">
备注:</div>
<divclass="editor-field">
@Html.EditorFor(model=>model.Remark)
@Html.ValidationMessageFor(model=>model.Remark)</div>

<p>
<inputtype="submit"value="添加"/>
</p>
</fieldset>}<div>
@Html.ActionLink("BacktoList","Index")</div>@sectionScripts{
@Scripts.Render("~/bundles/jqueryval")
}

  分析上面的代码:

  • @model EF_CodeFirstImp.Models.Book:指定该视图模板中的“模型”强类型化是一个Book类。

  • @using (Html.BeginForm()){ }:创建一个Form表单,在表单中包含了对于Book类所生成的对应字段。

  • @Html.EditorFor(model => model.BookName):根据模型生成模型中BookName的编辑控件(生成一个Input元素)

  • @Html.ValidationMessageFor(model => model.BookName):根据模型生成模型中BookName的验证信息。

  编译项目,在浏览器中输入http://localhost:2574/Book,然后点击”添加图书“来查看添加图书界面,运行结果如下图所示:

  步骤六:添加Create的Postback方法

  添加Create视图后,此时仅仅是把添加图书界面显示出来,并不能实际的完成图书的添加操作,因为我们还没有添加按钮的处理方法,没有实际的处理添加事件,为了完成数据的添加,在BookController类添加一个Create的Postback方法,完整的BookController代码如下所示:

复制代码

publicclassBookController:Controller
{readonlyBookDbContext_db=newBookDbContext();//
//GET:/Book/

publicActionResultIndex()
{varbooks=frombin_db.Bookswhereb.Author=="Learninghard"
selectb;returnView(books.ToList());
}publicActionResultCreate()
{returnView();
}

[HttpPost]publicActionResultCreate(Bookbook)
{if(ModelState.IsValid)
{
_db.Books.Add(book);
_db.SaveChanges();returnRedirectToAction("Index");
}else
returnView(book);
}

}

复制代码

  这时,我们在添加图书界面中输入数据,并点击”添加“按钮时,数据库中就会添加一行记录。打开数据库,我们将看到如下截图的数据:

  步骤七:设置视图模型的数据验证

  我们可以在模型类中显式地追加一个验证规则,使得对输入数据进行验证。修改之前的Book类为如下:

复制代码

usingSystem.ComponentModel.DataAnnotations;//需要额外添加该命名空间publicclassBook
{publicintBookId{get;set;}
[Required(ErrorMessage="必须输入图书名称")]
[StringLength(maximumLength:100,MinimumLength=1,ErrorMessage="最多允许输入100个字符")]publicstringBookName{get;set;}
[Required(ErrorMessage="必须输入作者名称")]publicstringAuthor{get;set;}
[Required(ErrorMessage="必须输入出版社名称")]publicstringPublisher{get;set;}publicdecimalPrice{get;set;}publicstringRemark{get;set;}
}

复制代码

  此时重新运行,并打开添加图书页面,当不输入任何数据的时候,点击”添加“按钮时,界面会出现一些提示信息,并阻止我们进行数据的提交操作,具体的结果界面如下所示:

  另外,EF创建数据库除了在第三步中添加连接字符串的方式外,还可以定义defaultConnectionFactory中添加parameters节点的方式来完成,dax.net中ByteartRetail项目中就是采用了这种方式。下面注释掉connectionStrings节点,在defaultConnectionFactory添加如下parameters节点:

复制代码

<entityFramework>
<defaultConnectionFactorytype="System.Data.Entity.Infrastructure.SqlConnectionFactory,EntityFramework">
<parameters>
<parametervalue="DataSource=(LocalDb)\v11.0;InitialCatalog=BookDB_2;IntegratedSecurity=True;AttachDBFilename=|DataDirectory|\BookDB_2.mdf"/>
</parameters>
</defaultConnectionFactory>
<providers>
<providerinvariantName="System.Data.SqlClient"type="System.Data.Entity.SqlServer.SqlProviderServices,EntityFramework.SqlServer"/>
</providers>
</entityFramework>

复制代码

  entityFramework节点是使用Nuget添加Entity Framework后自动添加的节点。下面测试下这种方案是否可以成功生成BookDB_2数据库呢?

  运行项目,在浏览器中输入地址:http://localhost:2574/Book,显示界面成功后,你将在你的App_Data目录下看到如下截图:

  从上图可以发现,这种方式同样成功生成了数据库。

三、总结

  到这里,领域驱动设计实战系列的前期准备就结束了,本文主要介绍了如何使用EF CodeFirst的功能自动生成数据库、以及实体的添加、查看操作等。这里也简单介绍了MVC相关内容。下一专题将介绍如何利用DDD的思想来构建一个简单的网站,接下来的系列就逐一加入DDD的概念来对该网站进行完善。

  本文所有源码下载:EFCodeFirstImp.zip

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结