ASP.NET MVC springmvc的执行流程程?

ASP.NET MVC:Filter和Action的执行介绍
字体:[ ] 类型:转载 时间:
ASP.NET MVC之Filter和Action的执行介绍,需要的朋友可以参考
根据controller的名字正确的实例化了一个controller对象。回到MVCHandler的BeginProcessRequest方法,可以看到,当得到controller对象之后,首先判断它是不是IAsyncController,如果是则会创建委托用来异步执行。通常情况下,我们都是继承自Controller类,这不是一个IAsyncController,于是会直接执行Controller的Execute方法。Execute方法是在Controller的基类ControllerBase中定义的,这个方法除去一些安全检查,初始化了ControllerContext(包含了ControllerBase和Request的信息),核心是调用了ExecuteCore方法,这在ControllerBase是个抽象方法,在Controller类中有实现:
代码如下: protected override void ExecuteCore() { PossiblyLoadTempData(); try { string actionName = RouteData.GetRequiredString("action"); if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) { HandleUnknownAction(actionName); } } finally { PossiblySaveTempData(); }}
这个方法比较简单,首先是加载临时数据,这仅在是child action的时候会出现,暂不讨论。接下来就是获取action的名字,然后InvokeAction, 这里的ActionInvoker是一个ControllerActionInvoker类型的对象,我们来看它的InvokeAction方法,
代码如下: public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } if (String.IsNullOrEmpty(actionName)) { throw new mon_NullOrEmpty, "actionName"); } ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext); ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName); if (actionDescriptor != null) { FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor); try { AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor); if (authContext.Result != null) { // the auth filter signaled that we should let it short-circuit the request InvokeActionResult(controllerContext, authContext.Result); } else { if (controllerContext.Controller.ValidateRequest) { ValidateRequest(controllerContext); } IDictionary&string, object& parameters = GetParameterValues(controllerContext, actionDescriptor); ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters); InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result); } } catch (ThreadAbortException) { // This type of exception occurs as a result of Response.Redirect(), but we special-case so that // the filters don't see this as an error.
} catch (Exception ex) { // something blew up, so execute the exception filters ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex); if (!exceptionContext.ExceptionHandled) {
} InvokeActionResult(controllerContext, exceptionContext.Result); }
} // notify controller that no method matched }
这是一个非常核心的方法,有很多工作在这里面完成。ASP.NET MVC中有几个以Descriptor结尾的类型,首先获得ControllerDescriptor,这个比较简单,实际返回的是ReflectedControllerDescriptor对象。第二步实际上是调用了ReflectedControllerDescriptor的FindAction方法,获得ActionDescriptor,ActionDescriptor最重要的属性是一个MethodInfo,这就是当前action name对应的Action的方法。FindAction方法内部实际上是调用了ActionMethodSelector的FindActionMethod来获得MethodInfo,可以想象,这个方法将会反射controller的所有方法的名字,然后和action name匹配,实际上,ASP.NET还支持一些额外的功能,主要是: 1.通过ActionNameAttribute属性重命名action的名字;2.支持ActionMethodSelectorAttribute对action方法进行筛选,比如[HttpPost]之类的。下面简单看下ActionMethodSelector的实现,大致分为4步,首先是在构造函数中调用了如下方法反射controller中的所有action方法:
代码如下: private void PopulateLookupTables() { MethodInfo[] allMethods = ControllerType.GetMethods(BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public); MethodInfo[] actionMethods = Array.FindAll(allMethods, IsValidActionMethod); AliasedMethods = Array.FindAll(actionMethods, IsMethodDecoratedWithAliasingAttribute); NonAliasedMethods = actionMethods.Except(AliasedMethods).ToLookup(method =& method.Name, StringComparer.OrdinalIgnoreCase); }FindActionMethod方法如下: public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName) { List&MethodInfo& methodsMatchingName = GetMatchingAliasedMethods(controllerContext, actionName); methodsMatchingName.AddRange(NonAliasedMethods[actionName]); List&MethodInfo& finalMethods = RunSelectionFilters(controllerContext, methodsMatchingName); switch (finalMethods.Count) { case 0:
case 1: return finalMethods[0]; default: throw CreateAmbiguousMatchException(finalMethods, actionName); } } 这个方法是很清晰的,找到重命名之后符合的,本身名字符合的,然后所有的方法判断是否满足ActionMethodSelectorAttribute的条件,最后或者返回匹配的MethodInfo,或者抛出异常,或者返回null。三个步骤的实现并不困难,不再分析下去。 第三步是得到Filter。 FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);实际调用的是: FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor);这里的代码风格和之前的不太一样,特别喜欢用各种委托,读代码有点困难,估计不是同一个人写的。下面的分析都直接给出实际执行的代码。首先看下FilterProvider的构造函数:
代码如下: static FilterProviders() { Providers = new FilterProviderCollection(); Providers.Add(GlobalFilters.Filters); Providers.Add(new FilterAttributeFilterProvider()); Providers.Add(new ControllerInstanceFilterProvider()); }
回忆下ASP.NET给Action加上filter的方法一共有如下几种: 1. 在Application_Start注册全局filter 2. 通过属性给Action方法或者Controller加上filter 3. Controller类本身也实现了IActionFilter等几个接口。通过重写Controller类几个相关方法加上filter。 这三种方式就对应了三个FilterProvider,这三个Provider的实现都不是很困难,不分析了。到此为止,准备工作都好了,接下来就会执行Filter和Action,ASP.NET的Filter一共有4类:
Filter Type
Description
Authorization
IAuthorizationFilter
Runs first
IActionFilter
Runs before and after the action method
IResultFilter
Runs before and after the result is executed
IExceptionFilter
Runs if another filter or action method throws an exception下面看其源代码的实现,首先就是InvokeAuthorizationFilters:
代码如下: protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList&IAuthorizationFilter& filters, ActionDescriptor actionDescriptor) { AuthorizationContext context = new AuthorizationContext(controllerContext, actionDescriptor); foreach (IAuthorizationFilter filter in filters) { filter.OnAuthorization(context); if (context.Result != null) {
注意到在实现IAuthorizationFilter接口的时候,要表示验证失败,需要在OnAuthorization方法中将参数context的Result设置为ActionResult,表示验证失败后需要显示的页面。接下来如果验证失败就会执行context的Result,如果成功就要执行GetParameterValues获得Action的参数,在这个方法内部会进行Model Binding,这也是ASP.NET的一个重要特性,另文介绍。再接下来会分别执行InvokeActionMethodWithFilters和InvokeActionResultWithFilters,这两个方法的结构是类似的,只是一个是执行Action方法和IActionFilter,一个是执行ActionResult和IResultFilter。以InvokeActionMethodWithFilters为例分析下:
代码如下: protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList&IActionFilter& filters, ActionDescriptor actionDescriptor, IDictionary&string, object& parameters) { ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters); Func&ActionExecutedContext& continuation = () =& new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) { Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters) }; // need to reverse the filter list because the continuations are built up backward Func&ActionExecutedContext& thunk = filters.Reverse().Aggregate(continuation, (next, filter) =& () =& InvokeActionMethodFilter(filter, preContext, next)); return thunk(); }
这段代码有点函数式的风格,不熟悉这种风格的人看起来有点难以理解。 用函数式编程语言的话来说,这里的Aggregate其实就是foldr, foldr::(a-&b-&b)-&b-&[a]-&b foldr 接受一个函数作为第一个参数,这个函数的参数有两个,类型为a,b,返回类型为b,第二个参数是类型b,作为起始值,第三个参数是一个类型为a的数组,foldr的功能是依次将数组中的a 和上次调用第一个参数函数(f )的返回值作为f的两个参数进行调用,第一次调用f的时候用起始值。对于C#来说,用面向对象的方式表示,是作为IEnummerable的一个扩展方法实现的,由于C# 不能直接将函数作为函数的参数传入,所以传入的是委托。说起来比较拗口,看一个例子:
代码如下: static void AggTest() { int[] data = { 1, 2, 3, 4 }; var res = data.Aggregate("String", (str, val) =& str + val.ToString()); Console.WriteLine(res); }
最后输出的结果是String1234. 回到InvokeActionMethodWithFilters的实现上来,这里对应的类型a是IActionFilter,类型b是Func&ActionExecutedContext&,初始值是continuation。假设我们有3个filter,[f1,f2,f3],我们来看下thunk最终是什么, 第一次: next=continue, filter=f1, 返回值 ()=&InvokeActionMethodFilter(f1, preContext, continue) 第二次:next=()=&InvokeActionMethodFilter(f1, preContext, continue), filter=f2 返回值:()=&InvokeActionMethodFilter(f2, preContext,()=& InvokeActionMethodFilter(f1, preContext, continue)), 最终: thunk= ()=&InvokeActionMethodFilter(f3,preContext,()=&InvokeActionMethodFilter(f2, preContext, ()=&InvokeActionMethodFilter(f1, preContext, continue))); 直到 return thunk()之前,所有真正的代码都没有执行,关键是构建好了thunk这个委托,把thunk展开成上面的样子,应该比较清楚真正的调用顺序什么样的了。这里花了比较多的笔墨介绍了如何通过Aggregate方法构造调用链,这里有一篇文章专门介绍了这个,也可以参考下。想象下,如果filter的功能就是先遍历调用f的Executing方法,然后调用Action方法,最后再依次调用f的Executed方法,那么完全可以用迭代来实现,大可不必如此抽象复杂,关键是ASP.NET MVC对于filter中异常的处理还有一些特殊之处,看下InvokeActionMethodFilter的实现:
代码如下: internal static ActionExecutedContext InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func&ActionExecutedContext& continuation) { filter.OnActionExecuting(preContext); if (preContext.Result != null) { return new ActionExecutedContext(preContext, preContext.ActionDescriptor, true /* canceled */, null /* exception */) { Result = preContext.Result }; } bool wasError = ActionExecutedContext postContext = try { postContext = continuation(); } catch (ThreadAbortException) { // This type of exception occurs as a result of Response.Redirect(), but we special-case so that // the filters don't see this as an error. postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, null /* exception */); filter.OnActionExecuted(postContext);
} catch (Exception ex) { wasError = postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, ex); filter.OnActionExecuted(postContext); if (!postContext.ExceptionHandled) {
} } if (!wasError) { filter.OnActionExecuted(postContext); } return postC }
代码有点长,首先就是触发了filter的OnActionExecuting方法,这是方法的核心。接下来的重点是 postContext = continuation(); 最后是OnActionExecuted方法,结合上面的展开式,我们可以知道真正的调用顺序将是:
代码如下: f3.Executing-&f2.Executing-&f1.Exectuing-&InvokeActionMethod-&f1.Executed-&f2-&Executed-&f3.Executed.
那么,源代码中的注释 // need to reverse the filter list because the continuations are built up backward 的意思也很明了了。需要将filter倒序排一下之后才是正确的执行顺序。 还有一类filter是当异常发生的时候触发的。在InvokeAction方法中可以看到触发它的代码放在一个catch块中。IExceptionFilter的触发流程比较简单,不多做解释了。唯一需要注意的是ExceptionHandled属性设置为true的时候就不会抛出异常了,这个属性在各种context下面都有,他们是的效果是一样的。比如在OnActionExecuted方法中也可以将他设置为true,同样不会抛出异常。这些都比较简单,不再分析其源代码,这篇文章比较详细的介绍了filter流程中出现异常之后的执行顺序。 最后说下Action Method的执行,前面我们已经得到了methodInfo,和通过data binding获得了参数,调用Action Method应该是万事俱备了。asp.net mvc这边的处理还是比较复杂的,ReflectedActionDescriptor会去调用ActionMethodDispatcher的Execute方法,这个方法如下:
代码如下: public object Execute(ControllerBase controller, object[] parameters) { return _executor(controller, parameters); }
此处的_executor是 delegate object ActionExecutor(ControllerBase controller, object[] parameters);_exectuor被赋值是通过一个方法,利用Expression拼出方法体、参数,代码在(ActionMethodDispatcher.cs): static ActionExecutor GetExecutor(MethodInfo methodInfo)此处就不贴出了,比较复杂。这里让我比较费解的是,既然MethodInfo和parameters都有了,直接用反射就可以了,为什么还要如此复杂,我将上面的Execute方法改为:
代码如下: public object Execute(ControllerBase controller, object[] parameters) { return MethodInfo.Invoke(controller, parameters); //return _executor(controller, parameters); }
运行结果是完全一样的。我相信mvc源代码如此实现一定有其考虑,这个需要继续研究。 最后附上一张函数调用图,以便理解,仅供参考。图片较大,点击可看原图。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具&&&&&&正文
asp.net MVC4的执行流程
摘要:asp.net MVC4的执行流程
MVC在底层和传统的asp.net是一致的,在底层之上,相关流程如下:1)Global.asax里,MvcApplication对象的Application_Start()事件中,调用 RouteConfig.RegisterRoutes(RouteTable.Routes); 来注册路由规则。2)RouteConfig.RegisterRoutes()方法里,给出的默认规则为 {controller}/{action}/{id} .a. 在有特别需要的时候,到这里来修改规则。b. 未指明Controller默认为HomeContoller,未指定Action默认为Index, 也就是说,直接访问站点时,会默认导航到HomeController下的Index Action.c. Action后跟的内容会被映射为名为id的参数。 比如: User/Delete/1 会匹配到 UserController的 Delete(int id) 方法。d. Action分为[HttpGet]和[HttpPost]两种,与HTTP的GET和POST方式对应。 即直接请求的URL只会匹配[HttpGet]方法,而未注明[HttpPost]的方法即默认为[HttpGet]. Post的Action一般会有一个Model参数,MVC会自动将表单里的数据按约定的规则填充到Model实体对象里。 这个约定的规则,就是表单的Name与属性名称有个对应规则。3)MVC负责按上述规则将执行流程导入合适的Action中,即Controller的某个方法中。4)Action中,可以直接返回字符串,输出到浏览器, 也可以返回到ActionResult对象, 该对象指向一个View页面,并且附带了一些属性作为传递数据的容器。ActionResult通常通过调用Controller对象的View(…)方法得到,也可以通过Redirect()方法或 RedirectToAction()方法得到, 不过后面两种方法主要用于跳转到其它Controller,因为后面的方法不能传递Model对象。 Action向View传递数据,可以通过Model、ViewBag或ViewData三种容器,其中Model是强类型的,所以是最优先的方式。5)View中,会先执行本身,再执行模板页和子页, 但是执行顺序和页面内容的输出顺序并不一致。模板中后输出的内容可以在页面中先输出的内容的前面.在View中,需要提交数据,或跳转页面里,均会通过Route导向某个Controller的Action。 View中向Action中传递数据,其最终原理是通过http的GET和POST等方式(含AJAX的), POST方式会通过上面提到的约定规则,将表单中的数据填充到Model参数对象中。 在Action中,可以访问通过参数传递进来的Model对象,也可以访问原始的Request对象中的QueryString参数或Form集合, 当然Model对象是优先的方式。
全国校区查询
新手入门点击榜
读取xml文件转成List对象的两种方法
新手入门最新文章
官方新版意见收集
*您的积极反馈是我们前进的动力
官方新版意见收集
提交成功,感谢您的反馈。
我们会认真阅读和考虑每个用户的反馈。下次自动登录
现在的位置:
& 综合 & 正文
MVC中使用EF(1):为ASP.NET MVC程序创建Entity Framework数据模型
为ASP.NET MVC程序创建Entity Framework数据模型 (1 of 10)
原文:http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application
By |July 30, 2013
Translated by litdwg
Contoso University示例网站演示如何使用Entity Framework 5创建ASP.NET MVC 4应用程序。
Entity Framework有三种处理数据的方式: Database First, Model
First, and Code First. 本指南使用代码优先。其它方式请查询资料。
示例程序是为Contoso University建立一个网站。功能包括:学生管理、课程创建、教师分配。 本系列指南逐步讲述如何实现这一网站程序。
本示例程序基于 .如果使用 ASP.NET Web Forms model, 请查看 系列指南和 .
如有问题,可在这些讨论区提问: , the , or .
(此指南的旧版本请查看 .)
Contoso University 应用程序
本指南将创建一个简单的大学网站.
用户可查看或更新学生、课程、教师的信息,以下是相关截图:
UI风格延续了默认模板的风格,以便更多关注于如何使用Entity Framework。
or , 可从以下链接获取相关需求软件:
如果已经安装 Visual Studio,此链接将只安装缺少的组件.如果没有Visual Studio, 将安装Visual Studio 2012 Express for Web. 你也可使用Visual Studio 2013,但一些步骤和截图有所不同.
若使用 Visual Studio 2010,需要安装MVC 4 和 SQL Server LocalDB.
创建MVC Web程序
创建程序如下:
选择 Internet Application template.
选择 Razor
设置网站风格
菜单、布局有少许变动.
打开Views\Shared\_Layout.cshtml, 修改如下:黄色为修改后内容.
&!DOCTYPE html&
charset="utf-8"
@ViewBag.Title - Contoso University
href="~/favicon.ico" rel="shortcut icon" type="image/x-icon"
name="viewport" content="width=device-width"
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
class="content-wrapper"
class="float-left"
class="site-title"@Html.ActionLink("Contoso University", "Index", "Home")
class="float-right"
id="login"
@Html.Partial("_LoginPartial")
@Html.ActionLink("Home", "Index", "Home")
@Html.ActionLink("About", "About", "Home")
@Html.ActionLink("Students", "Index", "Student")
@Html.ActionLink("Courses", "Index", "Course")
@Html.ActionLink("Instructors", "Index", "Instructor")
@Html.ActionLink("Departments", "Index", "Department")
@RenderSection("featured", required: false)
class="content-wrapper main-content clear-fix"
@RenderBody()
class="content-wrapper"
class="float-left"
& @DateTime.Now.Year - Contoso University
@Scripts.Render("~/bundles/jquery")
@RenderSection("scripts", required: false)
上面做了两点改变:
把 "My ASP.NET MVC Application" 和"your logo here" 替换为"Contoso University".
添加一些后面用到的超链接
在Views\Home\Index.cshtml, 替换为如下代码:
ViewBag.Title = "Home Page";
@section featured {
class="featured"
class="content-wrapper"
class="title"
@ViewBag.Title.
@ViewBag.Message
在 Controllers\HomeController.cs, 把 ViewBag.Message 值替换为 "Welcome
to Contoso University!":
public ActionResult Index()
ViewBag.Message = "Welcome to Contoso University";
return View();
CTRL+F5 运行,界面如下.
创建数据模型
先创建如下三个数据模型:
Student and Enrollment 实体是一对多关系,, Course and Enrollment 实体也是一对多关系.
也就是说一个学生可以注册多门课程,一门课程允许多位学生注册。
为每个实体创建对应的类:
注意:在完成所有实体之前尝试编译,将导致编译失败.
Student Entity
在Models文件夹创建Student.cs ,代码如下:
using System;
using System.Collections.Generic;
namespace ContosoUniversity.Models
public class Student
public int StudentID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection&Enrollment& Enrollments { get; set; }
StudentID 属性将成为数据表的主键列。默认Entity Framework将名为ID或者类名ID的属性设为主键。
Enrollments 是一个导航属性。导航属性记录和本实体相关的其它实体。在本例中,Enrollments 属性记录和 Student 属性相关的Enrollment。.如果数据库中某Student记录和两条Enrollment记录 相关。(这两条记录的 StudentID 外键值等于该Student的StudentID) ,那么Student 实体的 Enrollments 导航属性将包含这两个 Enrollment 实体.
Navigation properties 常定义为virtual 以便发挥Entity Framework的功能,如 lazy
loading. (在
部分将详细讲述延迟加载).
如果navigation property 包含多记录 (如 many-to-many or one-to-many 关系), 类型最好是列表类型,如 ICollection.
The Enrollment Entity
在Models文件夹, 创建 Enrollment.cs,代码如下:
namespace ContosoUniversity.Models
public enum Grade
A, B, C, D, F
public class Enrollment
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
Grade类型 后面的问号表示Grade 属性是 .
StudentID property 是外键, 相应的导航属性是 Student.一个 Enrollment实体和一个 Student实体相关,
CourseID property是外键,
相应的导航属性是 Course.
The Course Entity
在Models文件夹, 创建Course.cs,
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
public class Course
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection&Enrollment& Enrollments { get; set; }
Enrollments 属性是导航属性. 一个 Course实体对应多个 Enrollment实体.
下一节再对 [(.None)] 特性进行讲解。
简而言之,此特性表明主键由你赋值而非数据库自动生成。
创建Database Context
将 Entity Framework 功能和给定数据模型相关联的类是 database context class. 此类继承自
class.代码表明数据模型包含了哪些实体类型.本项目中数据上下文类名为 SchoolContext.
创建 DAL文件夹 (for Data Access Layer). 在此文件夹创建SchoolContext.cs,代码如下:
using ContosoUniversity.Models;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace ContosoUniversity.DAL
public class SchoolContext : DbContext
public DbSet&Student& Students { get; set; }
public DbSet&Enrollment& Enrollments { get; set; }
public DbSet&Course& Courses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
modelBuilder.Conventions.Remove&PluralizingTableNameConvention&();
代码为每一个实体集合创建
在Entity Framework,实体集合对应数据表,一个实体对应表中的一条记录。.
modelBuilder.Conventions.Remove 语句阻止表名使用实体的复数形式,如果没有此语句,则生成的数据表分别是
Students, Courses,
andEnrollments. 使用此语句,表名将和实体名一样 Student, Course,
and Enrollment. 这和编程风格相关,至于是否使用复数取决于你自己。
SQL Server Express LocalDB
是一个轻量级的SQL
Server。这里不做翻译介绍。
打开根目录下的 Web.config 文件,在 connectionStrings 处添加连接字符串如下,(注意,如果没有localdb,而使用SQL
Server或者Express版本,请修改连接字符串)
name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUIntegrated Security=SSPI;AttachDBFilename=|DataDirectory|\ContosoUniversity.mdf" providerName="System.Data.SqlClient"
默认Entity Framework寻找和数据上下文类同名的连接字符串 (SchoolContext for this project). 更多连接字符串信息,请查看 .
也可不添加连接字符串,由程序自动生成。但会导致数据库文件没有放在程序的 App_data文件夹下,更多信息请查看
connectionStrings 集合默认包含一个名为 DefaultConnection的连接字符串,是用来连接
membership database. 这里不会用到。两条连接字符串唯一不同之处是数据库名字不同
设置并执行 Code First Migration
在程序初期,数据模型经常发生变动,每次变动就会导致和数据库不一致。可将Entity Framework配置为变动后自动重建数据库。但在程序使用之后如果发生变动,更希望是更新数据库而非重建(重建导致数据丢失)。 Migrations 功能使得代码优先方式下更新数据库。如果希望重建可使用实现每次变动后重建数据库。.
本例中我们直接使用Migration方法,更多信息请查看 .
启用Code First Migrations
工具菜单,选择Library Package Manager ,Package
Manager Console.
PM& 提示符下输入如下命令:
enable-migrations -contexttypename SchoolContext
命令将创建 Migrations文件夹,并在文件夹下创建Configuration.cs.
Configuration 类包含 Seed 方法,数据库创建或更新后将调用此方法。
internal sealed class Configuration : DbMigrationsConfiguration&ContosoUniversity.Models.SchoolContext&
public Configuration()
AutomaticMigrationsEnabled = false;
protected override void Seed(ContosoUniversity.Models.SchoolContext context)
This method will be called after migrating to the latest version.
You can use the DbSet&T&.AddOrUpdate() helper extension method
to avoid creating duplicate seed data. E.g.
context.People.AddOrUpdate(
p =& p.FullName,
new Person { FullName = "Andrew Peters" },
new Person { FullName = "Brice Lambson" },
new Person { FullName = "Rowan Miller" }
Seed 方法使得可设置自动插入到数据库中的数据
设置Seed方法
为了便于测试,我们在Seed中添加一些数据
替换 Configuration.cs内容如下:
namespace ContosoUniversity.Migrations
using System;
using System.Collections.Generic;
using System.Data.Entity.Migrations;
using System.Linq;
using ContosoUniversity.Models;
internal sealed class Configuration : DbMigrationsConfiguration&ContosoUniversity.DAL.SchoolContext&
public Configuration()
AutomaticMigrationsEnabled = false;
protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
var students = new List&Student&
new Student { FirstMidName = "Carson",
LastName = "Alexander",
EnrollmentDate = DateTime.Parse("") },
new Student { FirstMidName = "Meredith", LastName = "Alonso",
EnrollmentDate = DateTime.Parse("") },
new Student { FirstMidName = "Arturo",
LastName = "Anand",
EnrollmentDate = DateTime.Parse("") },
new Student { FirstMidName = "Gytis",
LastName = "Barzdukas",
EnrollmentDate = DateTime.Parse("") },
new Student { FirstMidName = "Yan",
LastName = "Li",
EnrollmentDate = DateTime.Parse("") },
new Student { FirstMidName = "Peggy",
LastName = "Justice",
EnrollmentDate = DateTime.Parse("") },
new Student { FirstMidName = "Laura",
LastName = "Norman",
EnrollmentDate = DateTime.Parse("") },
new Student { FirstMidName = "Nino",
LastName = "Olivetto",
EnrollmentDate = DateTime.Parse("") }
students.ForEach(s =& context.Students.AddOrUpdate(p =& p.LastName, s));
context.SaveChanges();
var courses = new List&Course&
new Course {CourseID = 1050, Title = "Chemistry",
Credits = 3, },
new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3, },
new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3, },
new Course {CourseID = 1045, Title = "Calculus",
Credits = 4, },
new Course {CourseID = 3141, Title = "Trigonometry",
Credits = 4, },
new Course {CourseID = 2021, Title = "Composition",
Credits = 3, },
new Course {CourseID = 2042, Title = "Literature",
Credits = 4, }
courses.ForEach(s =& context.Courses.AddOrUpdate(p =& p.Title, s));
context.SaveChanges();
var enrollments = new List&Enrollment&
new Enrollment {
StudentID = students.Single(s =& s.LastName == "Alexander").StudentID,
CourseID = courses.Single(c =& c.Title == "Chemistry" ).CourseID,
Grade = Grade.A
new Enrollment {
StudentID = students.Single(s =& s.LastName == "Alexander").StudentID,
CourseID = courses.Single(c =& c.Title == "Microeconomics" ).CourseID,
Grade = Grade.C
new Enrollment {
StudentID = students.Single(s =& s.LastName == "Alexander").StudentID,
CourseID = courses.Single(c =& c.Title == "Macroeconomics" ).CourseID,
Grade = Grade.B
new Enrollment {
StudentID = students.Single(s =& s.LastName == "Alonso").StudentID,
CourseID = courses.Single(c =& c.Title == "Calculus" ).CourseID,
Grade = Grade.B
new Enrollment {
StudentID = students.Single(s =& s.LastName == "Alonso").StudentID,
CourseID = courses.Single(c =& c.Title == "Trigonometry" ).CourseID,
Grade = Grade.B
new Enrollment {
StudentID = students.Single(s =& s.LastName == "Alonso").StudentID,
CourseID = courses.Single(c =& c.Title == "Composition" ).CourseID,
Grade = Grade.B
new Enrollment {
StudentID = students.Single(s =& s.LastName == "Anand").StudentID,
CourseID = courses.Single(c =& c.Title == "Chemistry" ).CourseID
new Enrollment {
StudentID = students.Single(s =& s.LastName == "Anand").StudentID,
CourseID = courses.Single(c =& c.Title == "Microeconomics").CourseID,
Grade = Grade.B
new Enrollment {
StudentID = students.Single(s =& s.LastName == "Barzdukas").StudentID,
CourseID = courses.Single(c =& c.Title == "Chemistry").CourseID,
Grade = Grade.B
new Enrollment {
StudentID = students.Single(s =& s.LastName == "Li").StudentID,
CourseID = courses.Single(c =& c.Title == "Composition").CourseID,
Grade = Grade.B
new Enrollment {
StudentID = students.Single(s =& s.LastName == "Justice").StudentID,
CourseID = courses.Single(c =& c.Title == "Literature").CourseID,
Grade = Grade.B
foreach (Enrollment e in enrollments)
var enrollmentInDataBase = context.Enrollments.Where(
s.Student.StudentID == e.StudentID &&
s.Course.CourseID == e.CourseID).SingleOrDefault();
if (enrollmentInDataBase == null)
context.Enrollments.Add(e);
context.SaveChanges();
由于此方法在创建或更新后调用,为了避免多次插入同一数据,调用方法,第一个参数用来检查数据是否已经存在。
context.Students.AddOrUpdate(p =& p.LastName, s)
关于更多信息,请查看
foreach (Enrollment e in enrollments)
var enrollmentInDataBase = context.Enrollments.Where(
s =& s.Student.StudentID == e.Student.StudentID &&
s.Course.CourseID == e.Course.CourseID).SingleOrDefault();
if (enrollmentInDataBase == null)
context.Enrollments.Add(e);
关于Seed中问题的调试,请查看
创建并执行 First Migration
在 the Package Manager Console 执行命令:
add-migration InitialCreate
update-database
add-migration 命令将添加 [DateStamp]_InitialCreate.cs 文件到Migrations文件夹,文件中包含数据库创建初始化信息。第一个参数
(InitialCreate) 作为文件名,前面会加上时间戳.
InitialCreate 文件代码如下:
namespace ContosoUniversity.Migrations
using System;
using System.Data.Entity.Migrations;
public partial class InitialCreate : DbMigration
public override void Up()
CreateTable(
"dbo.Student",
StudentID = c.Int(nullable: false, identity: true),
LastName = c.String(),
FirstMidName = c.String(),
EnrollmentDate = c.DateTime(nullable: false),
.PrimaryKey(t =& t.StudentID);
CreateTable(
"dbo.Enrollment",
EnrollmentID = c.Int(nullable: false, identity: true),
CourseID = c.Int(nullable: false),
StudentID = c.Int(nullable: false),
Grade = c.Int(),
.PrimaryKey(t =& t.EnrollmentID)
.ForeignKey("dbo.Course", t =& t.CourseID, cascadeDelete: true)
.ForeignKey("dbo.Student", t =& t.StudentID, cascadeDelete: true)
.Index(t =& t.CourseID)
.Index(t =& t.StudentID);
CreateTable(
"dbo.Course",
CourseID = c.Int(nullable: false),
Title = c.String(),
Credits = c.Int(nullable: false),
.PrimaryKey(t =& t.CourseID);
public override void Down()
DropIndex("dbo.Enrollment", new[] { "StudentID" });
DropIndex("dbo.Enrollment", new[] { "CourseID" });
DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
DropForeignKey("dbo.Enrollment", "CourseID", "dbo.Course");
DropTable("dbo.Course");
DropTable("dbo.Enrollment");
DropTable("dbo.Student");
The update-database 运行文件中的 Up 方法创建数据库,然后调用 Seed方法.
名为 ContosoUniversity的数据库将被创建,
.mdf文件被存放在 App_Data 文件夹,如你在连接字符串指定的一致.
以下步骤是在VS中查看数据库的操作,按图所示操作即可,不再翻译。
From the View menu, click Server
Click the Add Connection icon.
If you are prompted with the Choose Data Source dialog, click Microsoft
SQL Server, and then clickContinue.
In the Add Connection dialog box,
(localdb)\v11.0 for
the Server Name. Under Select
or enter a database name, select ContosoUniversity.
SchoolContext and then expand Tables.
Right-click the Student table and click Show
Table Data to see the columns that were created and the rows that were inserted into the table.
创建Student Controller and Views
右击Controllers文件夹,选择创建Controller,相关参数信息如下图所示:
Visual Studio 打开 Controllers\StudentController.cs file. 数据库上下文对象已经创建
private SchoolContext db = new SchoolContext();
Index action method 从数据库上下文获取 Students属性,返回学生列表:
public ViewResult Index()
return View(db.Students.ToList());
The Student\Index.cshtml 视图显示了列表中的信息:
@Html.DisplayNameFor(model =& model.LastName)
@Html.DisplayNameFor(model =& model.FirstMidName)
@Html.DisplayNameFor(model =& model.EnrollmentDate)
@foreach (var item in Model) {
@Html.DisplayFor(modelItem =& item.LastName)
@Html.DisplayFor(modelItem =& item.FirstMidName)
@Html.DisplayFor(modelItem =& item.EnrollmentDate)
@Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) |
@Html.ActionLink("Details", "Details", new { id=item.StudentID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.StudentID })
Press CTRL+F5 运行。
点击Students 查看。
EF的这些惯例,使得以上所写代码不多:
实体类名的复数形式作为表名.
实体类的属性名作为表的列名.
ID 或 classnameID 作为主键.
惯例可以不必遵守(如本文不用复数形式作为表名),如果使用惯例或者覆盖惯例,请查看后面的
。更多信息请查看. .
使用 Entity Framework 和SQL Server Express 创建了一个简单的web程序。随后将学习如何完成基本的 CRUD (create, read, update, delete) 操作.
&&&&推荐文章:
【上篇】【下篇】

我要回帖

更多关于 活动执行流程 的文章

 

随机推荐