mvc dbfirst mvc模型层浏览器"在哪

SocialGoal - ASP.NET MVC 5, EF 6 Code First, AutoMapper, Autofac, TDD and DDD Sample Site
16 ratings
Sorry, an error occurred.
Description
Today's Web Wednesday project from Shiju Varghese and team is one that brings together a pretty darn big number of different technologies together into a pretty complete &Social& sample reference site.Here's a list of juASP.NET MVC 5EF 6 Code FirstAutoMapperAutofacTwitter BootstrapNUnitMoqMy team at , developed and released a sample reference web app for the Microsoft ASP.NET stack, named “SocialGoal” . SocialGoal is a social networking web app for socializing your goals and strategies with people. People can create goals and can be shared across the network of SocialGoal users. This app is built with ASP.NET MVC 5, EF 6 Code First, AutoMapper, Autofac, Twitter Bootstrap and following Test-Driven Development (TDD) and Domain Driven Design (DDD). The first drop of the application is available on github at . A release note is available in the docs folder of the project repository....SocialGoal is a social networking web app for socializing your goals and strategies with people. The primary objective of the SocialGoal app is to provide a learning app for building real-world web apps with ASP.NET MVC 5 and EF 6 Code First. The application architecture is inspired from . SocialGoal is developed by .TechnologiesASP.NET MVC 5EF 6 Code FirstAutoMapperAutofacTwitter BootstrapNUnitMoqPatterns & PracticesDomain Driven Design (DDD)Test-Driven Development (TDD)Repository Pattern & Generic RepositoryUnit of WorkDependency InjectionGoals and RoadmapOverall Project GoalsWeb app for Social Networking for soclialize your goals and strategies.A reference web app for ASP.NET MVC 5 and EF 6 Code First.Improve developer productivity for building web apps on the Microsoft Web stack.Reference app for building Test-Driven Development (TDD) and Domain-Driven Design (DDD).Mobilize an existing app for solving the mobility challenges.Roadmap TargetsBuild a full-fledged social networking app with enhanced UI and new features.Mobilize the existing app Provide an API for Mobility, by using ASP.NET Web API 2.Build cross platform, minimalist mobile apps by using HTML5/JavaScript platform.Build Mobile Backend as a Service (MBaaS) solution on the Windows Azure for the mobile apps.From the Doc'sHow to build1. Clone the source code from GitHub and open the solution in Visual Studio 2013.2. Build the solution. It will automatically restore NuGet and will install NuGet packages.Note: The source code is excluded NuGet packages in the source control.IntroductionSocialGoal is a social networking web app for socializing your goals and strategies with people. People can create goals and can be shared across the network of SocialGoal users. Goals can have start date, end date, metrics and target. People can update their goals and others can see those updates and can comment on each updates. People can support any public goal.Groups can be created within SocialGoal so that it is very easy to build goals for a team. Group goals can be assigned to any of the group members. Groups can have focus and group goal can be created with a particular focus also.Features of SocialGoalUser should Register to start using SocialGoal applicationCreating Goals –User can set goals in two types1) Individual goals2) Goals for a group in which the user is a member.Edit & Delete Functionality is provided for each goal.Update Status-Updates can be given for users own goal with or without status.Comment on an Update-Any user can comment on an update of a public goal created by any user.Searching & Supporting a Goal-User can search all the public goals in the search box provided and can support them. The user can follow a goal.Creating Group & Inviting Users –User can create a group and can invite others to join the groupInvitation can send in two ways-1) Can invite an existing user by searching with a user name.Those invitations will send to user’s my invitation list and from there user can accept the invitation2) Can invite a new user by sending an emailThat invitation will send to the inbox of the recipient’s mailid and from there can join to the social goal and accept the invitationFollowing Group-Once the User accepts the invitation to a group, then goals can be created for that group. He can invite other users and can create a focus for the group.Creating Focus For a Group-The group can have a focus and the creation of goals is completely dependent on that focusReport for a Goal-User can see the report of a goal based on update date and target date. Report will be generated for updates having status.Notifications –User can see the notifications of the goal & group which is public.User Profile Creation-User can create a profile and can edit the profile....Grabbing the latest drop, I had the site up and running very quickly (well mostly, I had to remember to start my SQLExpress service, which I usually have stopped... Gee, no wonder the site couldn't talk to its database... :/ )There's one initial account, User Name:Admin, Password:123456.There's a few bugs and missing files, but the site runs and provides a great example of how to use all those different technologies...
More episodes in this series
Related episodes
The DiscussionASP.NET&MVC3&快速入门-第六节&增加一个追加数据的方法和一个追加数据的视图
在本节中我们将要在数据库中追加并保存一些数据。我们将要创建一个表单以及一些表单输入控件,用来输入数据信息。当用户提交表单时将把这些用户输入的信息保存在数据库中。我们可以通过在浏览器中输入“http://localhost:xx/Movies/Create”这个URL地址来访问这个表单。
6.1 显示追加信息时所用表单
首先,我们需要在我们的MoviesController类中追加一个Create方法,该方法返回一个视图,该视图中包含了用户输入信息时所要用到的表单。
public ActionResult Create()&
return View();
现在让我们来实现这个Create方法中所要返回的视图,我们将在这个视图中向用户显示追加数据时所需要用到的表单。在Create方法中点击鼠标右键,并点击上下文菜单中的“添加视图”。
在“添加视图”对话框中选择“创建强类型视图”,将模型类指定为“Movie”,在支架模板中选择Create,如图6-1所示。
添加追加数据时所用视图
点击添加按钮,Views文件夹下的Movies文件夹中将会自动添加一个名为“Create.cshtml”的视图模板文件。因为你在支架模板中选择了“Create”,所以支架模板会在该视图模板文件中自动生成一些默认代码。打开该文件进行查看,在该文件中已经自动创建了一个HTML表单,以及一段用来显示校验时错误信息的文字。Visual Web
Developer检查Movies类,并自动创建与该类中每个属性相对应的&label&元素以及&input&元素。支架模板自动生成的创建数据所用视图中的代码如代码清单6-1所示。
@model MvcMovie.Models.Movie
ViewBag.Title = "Create";
&h2&Create&/h2&
src="@Url.Content("~/Scripts/jquery.validate.min.js")"
type="text/javascript"&&/script&
src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
type="text/javascript"&&/script&
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
&fieldset&
&legend&Movie&/legend&
class="editor-label"&
&&&&@Html.LabelFor(model
=& model.Title)
class="editor-field"&
&&&&&&&&&&&
@Html.EditorFor(model =&
model.Title)
&&&&&&&&&&&
@Html.ValidationMessageFor(model =&
model.Title)
class="editor-label"&
&&&&&&&&@Html.LabelFor(model
=& model.ReleaseDate)
class="editor-field"&
&&&&&&&&&&&
@Html.EditorFor(model =&
model.ReleaseDate)
&&&&&&&&&&&
@Html.ValidationMessageFor(model =&
model.ReleaseDate)
class="editor-label"&
&&&&&&&&&&&
@Html.LabelFor(model =&
model.Genre)
class="editor-field"&
&&&&&&&&&&&
@Html.EditorFor(model =&
model.Genre)
&&&&&&&&&&&
@Html.ValidationMessageFor(model =&
model.Genre)
class="editor-label"&
&&&&&&&&&&&
@Html.LabelFor(model =&
model.Price)
class="editor-field"&
&&&&&&&&&&&
@Html.EditorFor(model =&
model.Price)
&&&&&&&&&&&
@Html.ValidationMessageFor(model =&
model.Price)
&&&&&&&&&&&
&input type="submit" value="Create"
&/fieldset&
@Html.ActionLink("Back to List", "Index")
这段代码中使用了几个HTML帮助器的方法来帮助简化HTML标签的书写方法。Html.LabelFor帮助器用于显示字段名(”Title”,”ReleaseDate”,”Genre”或者”Price”)。Html.EditorFor帮助器用于显示一个提供给用户输入信息的HTML的&input&元素。Html.ValidationMessageFor帮助器用于显示一个针对属性的校验信息。请注意我们的视图模板的顶部有一个“@model
MvcMovie.Models.Movie”的声明,该声明将我们的视图模板中的“模型”强类型化成一个Movie类。
运行应用程序,在浏览器中输入“http://localhost:xx/Movies/Create”,浏览器中显示如图6-2所示。
图6-2 支架模板自动生成的视图
在书写中文网站或中文Web应用程序的时候,可以将代码清单6-1中的代码修改为代码清单6-2中所示代码。
代码清单6-2 将支架模板自动生成的代码进行汉化
@model MvcMovie.Models.Movie
ViewBag.Title = "追加电影信息";
&h2&追加电影信息&/h2&
src="@Url.Content("~/Scripts/jquery.validate.min.js")"
type="text/javascript"&&/script&
src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
type="text/javascript"&&/script&
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
&fieldset&
&legend&电影&/legend&
class="editor-label"&
&&&&&&&&&&&
class="editor-field"&
&&&&&&&&&&&
@Html.EditorFor(model =&
model.Title)
&&&&&&&&&&&
@Html.ValidationMessageFor(model =&
model.Title)
class="editor-label"&
&&&&&&&&&&&
class="editor-field"&
&&&&&&&&&&&
@Html.EditorFor(model =&
model.ReleaseDate)
&&&&&&&&&&&
@Html.ValidationMessageFor(model =&
model.ReleaseDate)
class="editor-label"&
&&&&&&&&&&
class="editor-field"&
&&&&&&&&&&&
@Html.EditorFor(model =&
model.Genre)
&&&&&&&&&&&
@Html.ValidationMessageFor(model =&
model.Genre)
class="editor-label"&
&&&&&&&&&&&
class="editor-field"&
&&&&&&&&&&
&@Html.EditorFor(model
=& model.Price)
&&&&&&&&&&&
@Html.ValidationMessageFor(model =&
model.Price)
&&&&&&&&&&&
&input type="submit" value="追加"
&/fieldset&
@Html.ActionLink("返回电影列表",
http://localhost:xx/Movies/Create”这个地址,浏览器中显示如图6-3所示。
图6-3 中文化后的追加电影信息画面
鼠标右击浏览器中显示的该页面,点击“显示源代码”,显示出来的源代码如代码清单6-3所示。(为了突出本节内容,只显示与本节中相关部分)。
代码清单6-3 追加电影信息画面在浏览器中的HTML代码
&meta http-equiv="Content-Type" content="text/
charset=utf-8"/&
&title&追加电影信息&/title&
&link href="/Content/Site.css" rel="stylesheet"
type="text/css" /&
&script src="/Scripts/jquery-
1.4.4.min.js"
type="text/javascript"&&/script&
&h2&追加电影信息&/h2&
src="/Scripts/jquery.validate.min.js"
type="text/javascript"&&/script&
src="/Scripts/jquery.validate.unobtrusive.min.js"
type="text/javascript"&&/script&
&form action="/Movies/Create"
method="post"&&&&
&fieldset&
&legend&电影&/legend&
class="editor-label"&
&&&&&&&&&&&
class="editor-field"&
&&&&&&&&&&&
&input class="text-box single-line" id="Title"
name="Title"
type="text" value="" /&
&&&&&&&&&&&
&span class="field-validation-valid"
data-valmsg-for="Title"
data-valmsg-replace="true"&&/span&
class="editor-label"&
&&&&&&&&&&&
class="editor-field"&
&&&&&&&&&&&
&input class="text-box single-line"
data-val="true"
data-val-required="The
ReleaseDate field is required."
id="ReleaseDate" name="ReleaseDate" type="text" value=""
&&&&&&&&&&&
class="field-validation-valid"
data-valmsg-for="ReleaseDate"
data-valmsg-replace="true"&
class="editor-label"&
&&&&&&&&&&
class="editor-field"&
&&&&&&&&&&&
&input class="text-box single-line" id="Genre"
name="Genre"
type="text" value="" /&
&&&&&&&&&&&
&span class="field-validation-valid"
data-valmsg-for="Genre"
data-valmsg-replace="true"/&
class="editor-label"&
&&&&&&&&&&&
class="editor-field"&
&&&&&&&&&&&
&input class="text-box single-line"
data-val="true"
data-val-number="The field Price must be a
data-val-required="The Price field is required."
id="Price"
name="Price" type="text" value=""
&&&&&&&&&&&
&span class="field-validation-valid"
data-valmsg-for="Price"
data-valmsg-replace="true"&&/span&
&&&&&&&&&&&
&input type="submit" value="追加"
&/fieldset&
&a href="/Movies"&返回电影列表&/a&
id="footer"&
从这段代码中可以看出,表单的action属性被设置为“/Movies/Create”,当点击追加按钮时会把表单中各文本框中的数据提交到服务器端进行保存。
6.2 处理HTTP-POST
&&到此为止,我们已经实现了显示追加数据所用表单的所需代码。我们的下一步是编写代码来进行表单提交到服务器后的处理。我们将要获取用户在数据库中输入的信息并且将它们作为一个新的Movie保存到数据库中。
为了实现这一处理,我们需要在MoviesController类中追加第二个Create方法。这个Create方法具有一个[HttpPost]属性,它意味着我们将要用它来处理提交到“/Movies/Create”这个URL地址的请求。另外,所有提交到“/Movies/Create”这个URL地址的非POST的请求(即GET请求)将被第一个Create方法进行处理,即简单地返回一个空的表单。
代码清单6-4中所示代码为MoviesController类中的两个Create方法的全部代码。
代码清单6-4 MoviesController类中的两个Create方法的全部代码
public ActionResult Create()
return View();
[HttpPost]
public ActionResult Create(Movie
if (ModelState.IsValid)
db.Movies.Add(newMovie);
db.SaveChanges();
return RedirectToAction("Index");
return View(newMovie);
之前我们介绍了ASP.NET
MVC可以自动地将一个URL地址中的查询字符串中的参数(例如:传递到“/HelloWorld/Welcome?name=Scott&numTimes=5”)作为一个方法的参数传递到方法中。同样地,除了传递查询字符串中的参数之外,ASP.NET
MVC也可以用这种方法来传递提交后的表单参数。
提交后的表单参数可以作为一个独立的参数传递到一个方法中。例如,ASP.NET MVC
framework可以将我们提交的表单中的控件值作为参数传递到具有HttpPost属性的Create方法中,如下所示。
[HttpPost]
ActionResult Create(string title, DateTime releaseDate, string
decimal price)
提交的表单值也可以被映射到一个复合的,具有属性的对象(譬如我们的Movie类),并且作为一个单一的参数传递到一个方法中。在代码清单6-4中我们使用的就是这个方法。请注意Create方法是怎样作为一个参数来接收Movie对象的。
[HttpPost]
public ActionResult Create(Movie
if (ModelState.IsValid)
&&db.Movies.Add(newMovie);
&&&&&&&&&&&
db.SaveChanges();
&&&&&&&&&&&
return RedirectToAction("Index");
&&&&&&&&&&&
return View(newMovie);
属性用来检查提交的表单中的数据是否能够被用来创建一个Movie对象。如果数据是有效的,我们的代码将把提交上来的这个Movie类追加在MoviesDBContext对象的实例中的Movies集合中。调用我们的MoviesDBContext对象实例的SaveChanges方法将把这个Movie对象保存在数据库中。保存数据完毕后,代码把画面重定向到MoviesController类的Index方法中,浏览器中将会打开电影清单显示画面,并且在电影清单中显示刚才追加的这条数据。
追加一条电影信息
运行应用程序,在浏览器中输入“http://localhost:xx/Movies/Create”,在表单中输入一条电影信息,然后点击追加按钮,如图6-4所示。
图6-4 追加电影信息
点击追加按钮进行提交,表单中输入的这条电影信息将会保存到数据库中,保存后浏览器中将打开电影清单画面,并且将这条追加的电影显示在清单中,如图6-5所示。
图6-5 追加后电影将显示在电影清单中
你可能已经注意到了在这个电影清单画面中将刚才追加的电影票价显示成了10元,而不是用户输入的9.99元,这是因为当前该数据表中Decimal类型的默认精度只能识别与处理整数值,并且自动将小数部分四舍五入。关于如何解决这个问题,我们将在下一节中对模型进行一些调整的时候会同时进行介绍。
现在我们已经有了一个Web应用程序的雏形,我们可以在数据库中追加数据,显示数据。代码清单6-5是现在这个MoviesController类的完整代码。
代码清单6-5 MoviesController类的完整代码
using MvcMovie.M
using System.L
using System.Web.M
namespace MvcMovie.Controllers
public class MoviesController : Controller
MovieDBContext db = new MovieDBContext();
public ActionResult Index()
&&&&&&&&&&&
var movies=from m in db.Movies
&&&&&&&&&&&&&&&&&&&&&&
where m.ReleaseDate&new
DateTime()
&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&
return View(movies.ToList());
public ActionResult Create()
&&&&&&&&&&&
return View();
&&&&&[HttpPost]
public ActionResult Create(Movie newMovie)
&&&&&&&&&&&
if (ModelState.IsValid)
&&&&&&&&&&&
&&&&&&&&&&&&&&&
db.Movies.Add(newMovie);
&&&&&&&&&&&&&&&
db.SaveChanges();
&&&&&&&&&&&&&&&
return RedirectToAction("Index");
&&&&&&&&&&&
&&&&&&&&&&&&&&&
return View(newMovie);
在下一节,我们将介绍如何为我们的模型添加附加的属性,如何在映射后的数据库中定制我们的票价列的精度。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。使用EF DataBaseFirst做一个简单的MVC3报名网站 - CSDN博客
使用EF DataBaseFirst做一个简单的MVC3报名网站
使用EF DataBaseFirst做一个简单的MVC3报名网站
ORM(ObjectRelational Mapping)是面向对象语言中的一种数据访问技术,在ASP.NET中,可以通过ADO.NET Entity Framework技术来简化数据访问。在EF里,有Code First,Model First和DataBaseFirst三种方法来实现。
百度百科关于ORM的介绍:
1、就像EF DataBaseFirst名字所表示的意义,我们首先需要把我们需要传输数据的数据库建好,后边就要通过这个数据库来生成我们在MVC程序中所需要的实体类。我使用SQL Server 2008建好的一个简单数据库如下所示:
2、接下来我们将使用VisualStudio 2010来建一个MVC3 Web应用程序
文件→新建项目→如下图:
项目建立成功之后如下:
如果不了解MVC3的基本思想,可以参考一下微软官方的一个教程,Music Store
3、根据数据库生成实体数据模型edmx,步骤如下:
点击确定之后就会在VS的解决资源管理器中看到Models文件夹下多了一个实体数据模型
4、生成了数据实体之后,EF的代码生成器已经生成了我们所需要的类,但是这些类比较复杂,代码比较多,我们可以使用一个简单的模板来生成一些简单的数据上下文类,如下所示:
首先在空白处点击右键,点击添加代码生成项:
(在VS2010中的模板里没有这个EF 4.x数据上下文代码生成器,你可以在联机模板里搜索进行安装,安装之后以后就都可以用了)
之后会在解决方案资源管理器里看到多了两个类,如下:
这两个类中的代码如下:
5、接下来我们就来添加控制器和相应的视图,如下所示:
(在给控制器命名的时候,一定要在最后加上Controller,因为在MVC中有一个重要的原则就是约定大于配置,遵守这些约定可以让我们少写很多代码,因为在MVC中约定Home控制器,Index方法是默认的访问路径,我们就把新添加的控制器叫做Home,在Home控制器里添加一个Index方法,之后你还会发现我们从方法中生成的视图也是和我们的控制器和方法的名字是有关系的,控制器的名字是视图文件夹Views下的一个子文件夹,而方法对应着一个视图文件)
(要想用刚才生成的类,我们首先就要在代码最前边加上一个using 那些类的命名空间)
要在前端代码里使用那些类,要生成一下程序,按F6或者是
在Index方法里右键添加视图,添加前端代码,这些前端代码可以是纯的前端代码,也可以是使用HtmlHelper来生成,在MusicStore那个教程里有介绍
在新生成的视图代码里加上一个简单的提交表单,如下:
6、运行程序进行测试
按F5运行程序
在表单中输入数据后点击提交,到数据库里刷新之后发现新加进来一条数据
上边的例子里很多东西都没有考虑,如果要考虑数据的合法性验证和权限的限制可以参考微软音乐商店的那个例子和《精通ASP.NET MVC3框架》这本书
下边的这两个链接讲的也很详细,也可以参考一下这两个例子
本文已收录于以下专栏:
相关文章推荐
从前端的UI开始
MVC分离的比较好,开发顺序没有特别要求,先开发哪一部分都可以,这次我们主要讲解前端UI的部分。
ASP.NET MVC抛弃了WebForm的一些特有的习惯,例如服务器端控件,...
ADO.NET Entity Framework 是微软以 ADO.NET 为基础所发展出来的对象关系对应 (O/R Mapping) 解决方案,现已经包含在 Visual Studio 2008 S...
1  数据库建好
2建一个mvc项目
3  建一个ado.net 实体数据模型  命名为DataContent
  
4:连接好数据库 表
上篇博客中介绍了ORM思想,可以说ORM思想在数据交互方面给我们带来了一次变革。他能够自动实现Entity实体的属性与关系型数据库字段的映射,增删改查的sql脚本由ORM来自动生成,使我们编码时不用考...
★抽奖腾讯备案电话【↘↘7551】抽 奖 二 线【↘↘7551】活 动 热 线【↘↘7551】 幸 运 用 户 必 须 遵 守 领 奖 程 序 办...
public ActionResult Delete(int? id)
if (id == null)
1、首先创建一个空白Android项目2、然后打开项目切换为为Android视图,这时候会看到三个文件夹,分别是manifests、java、res。
首先修改src/main/res下的activ...
本文主要讲述了如何使用Multiplayer Networking开发多人游戏,文中实例、代码来源于Unity官方教程。
原文:INTRODUCTION TO A SIMPLE MULTIPLAY...
他的最新文章
讲师:何宇健
讲师:董岩
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)Mocking your Entity Framework data context and testing it in .NET MVC
I explained in my previous post& why you might want to mock your Entity Framework context rather than using the repository pattern. In this post I'll show you how to do that and how to test your .NET MVC controllers with a fake data context.
The way you do this varies slightly depending on whether you're using
or . I'll cover both in this post.
Using Code First in Entity Framework 4
My implementation of code first is based heavily on the .
A mockable data context (EF Code First)
When you create your code first DBContext it is very easy to make it include an interface that can be mocked. Here is a simple example:
public interface IContext
IDbSet&Foo& Foos { }
IDbSet&Bar& Bars { }
int SaveChanges();
public class EFContext : DbContext, IContext
public IDbSet&Foo& Foos { }
public IDbSet&Bar& Bars { }
Using you mockable context in a controller (EF Code First)
This is also very easy. I&d recommend using a proper dependency injection framework like AutoFac, but in this example I&m going to use to use poor mans DI to keep it simple.
public class HomeController
private IContext _data { }
public HomeController(IContext dataContext = null)
_data = dataContext ?? new EFContext();
public ViewResult Index()
var items = _data.Foos
.Include(f =& f.FooBit)
.ToList();
return View(items);
The nice thing here is we&ve been able to specify that we want to bring down information from the related entity FooBit. This means our view can show information from both the list of Foo objects and all the separate FooBits without creating a N+1 problem.
Unit testing our controller (EF Code First)
For this to work we need to a fake implementation of IDBSet. I&ve borrowed this :
public class FakeDbSet&T& : IDbSet&T&
where T : class
HashSet&T& _
IQueryable _
public FakeDbSet()
_data = new HashSet&T&();
_query = _data.AsQueryable();
public virtual T Find(params object[] keyValues)
throw new NotImplementedException("Derive from FakeDbSet&T& and override Find");
public void Add(T item)
_data.Add(item);
public void Remove(T item)
_data.Remove(item);
public void Attach(T item)
_data.Add(item);
public void Detach(T item)
_data.Remove(item);
Type IQueryable.ElementType
get { return _query.ElementT }
System.Linq.Expressions.Expression IQueryable.Expression
get { return _query.E }
IQueryProvider IQueryable.Provider
get { return _query.P }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
return _data.GetEnumerator();
IEnumerator&T& IEnumerable&T&.GetEnumerator()
return _data.GetEnumerator();
Now you've got a fake IDBSet you can create a simple test for our controller. I've used NUnit and .
public void IndexReturnsList()
// Create fake data
var inMemoryItems = new FakeDbSet&Foos&
new Foo {Id = 1, Title = "One"},
new Foo {Id = 2, Title = "Two"},
new Foo {Id = 3, Title = "Three"},
new Foo {Id = 4, Title = "Four"}
// Create mock unit of work
var mockData = Substitute.For&IContext&();
mockData.Foos.Returns(inMemoryItems);
// Setup controller
var homeController = new HomeController(mockData);
var viewResult = homeController.Index();
var foosFromView =
(IEnumerable&Foo&)viewResult.M
Assert.NotNull(foosFromView);
Assert.AreEqual(4, foosFromView.Count());
Assert.AreEqual(1, foosFromView.First().Id);
And there you have it. Everything is tested, and we didn&t need a repository.
Using Database First in Entity Framework
My implementation of database first is based on this .
A mockable data context (EF Database First)
When you use the database first wizard the code generated for you is not so easy to mock. So you need to wrap it in a mockable wrapper. Here is an example:
public interface IContext
IObjectSet&Foo& Foos { }
IObjectSet&Bar& Bars { }
void SaveChanges();
Here is the wrapper implementation:
public class EFContext : IContext
private readonly EFEntities _
public EntityFrameworkUnitOfWork()
_data = new EFEntities();
public IObjectSet&Foo& Foos
return _data.CreateObjectSet&Foo&();
public IObjectSet&Bar& Bars
return _data.CreateObjectSet&Bar&();
public void SaveChanges()
_data.SaveChanges();
Using you mockable context in a controller (EF Database First)
This is exactly the same as with code first, but I've included it again for completeness. As I said before it's very easy. I'd recommend using a proper dependency injection framework like AutoFac, but in this example I&m going to use to use poor mans DI to keep it simple.
public class HomeController
private IContext _data { }
public HomeController(IContext dataContext = null)
_data = dataContext ?? new EFContext();
public ViewResult Index()
var items = _data.Foos
.Include(f =& f.FooBit)
.ToList();
return View(items);&&&
The nice thing here is we've been able to specify that we want to bring down information from the related entity FooBit. This means our view can show information from both the list of Foo objects and all the separate FooBits without creating a N+1 problem.
Unit testing our controller (EF Database First)
For this to work we need to a fake implementation of IObjectSet. I've borrowed this :
public class InMemoryObjectSet&T&
: IObjectSet&T& where T : class
public InMemoryObjectSet()
: this(Enumerable.Empty&T&())
public InMemoryObjectSet(IEnumerable&T& entities)
_set = new HashSet&T&();
foreach (var entity in entities)
_set.Add(entity);
_queryableSet = _set.AsQueryable();
public void AddObject(T entity)
_set.Add(entity);
public void Attach(T entity)
_set.Add(entity);
public void DeleteObject(T entity)
_set.Remove(entity);
public void Detach(T entity)
_set.Remove(entity);
public Type ElementType
get { return _queryableSet.ElementT }
public Expression Expression
get { return _queryableSet.E }
public IQueryProvider Provider
get { return _queryableSet.P }
public IEnumerator&T& GetEnumerator()
return _set.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
return GetEnumerator();
readonly HashSet&T& _
readonly IQueryable&T& _queryableS
Now you've got a fake IObjectSet you can create a simple test for our controller. I've used NUnit and .
public void IndexReturnsList()
// Create fake data
var fooList = new List&Foo&
new Foo {Id = 1, Name = "One"},
new Foo {Id = 2, Name = "Two"},
new Foo {Id = 3, Name = "Three"},
new Foo {Id = 4, Name = "Four"}
var inMemoryItems =
new InMemoryObjectSet&Foo&(fooList);
// Create mock unit of work
var mockData = Substitute.For&IContext&();
mockData.Foos.Returns(inMemoryItems);
var homeController = new HomeController(mockData);
var viewResult = homeController.Index();
var foosFromView =
(IEnumerable&Foo&)viewResult.M
Assert.NotNull(foosFromView);
Assert.AreEqual(4, foosFromView.Count());
Assert.AreEqual(1, foosFromView.First().Id);
And there you have it. Everything is tested, and we didn&t need a repository. It was a bit trickier with database first, but not much.
What about query logic?
Another reason to use a repository is to centralise your query logic that might be needed by various controllers. In my next post I will show you .
Noggin Box 2017, powered by .

我要回帖

更多关于 mvc 清除浏览器缓存 的文章

 

随机推荐