{"id":546,"date":"2010-05-14T19:34:12","date_gmt":"2010-05-14T17:34:12","guid":{"rendered":"http:\/\/dev.bratched.fr\/fr\/how-we-do-asp-net-mvc\/"},"modified":"2010-05-14T19:34:12","modified_gmt":"2010-05-14T17:34:12","slug":"comment-faire-aspnet-mvc","status":"publish","type":"post","link":"https:\/\/bratched.com\/fr\/2010\/05\/14\/comment-faire-aspnet-mvc\/","title":{"rendered":"Comment nous faisons de l&rsquo;ASP.NET MVC"},"content":{"rendered":"<h1>Sample MVC Solution<\/h1>\n<p>In this post I will show a sample ASP.NET MVC 2.0 project structure illustrating different concepts such as data access, user input validation and mapping between the domain and the view model. The project is still under construction but the <a href=\"http:\/\/github.com\/darind\/samplemvc\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">source code is available at github<\/a>.<\/p>\n<p>I will illustrate the usage of the following frameworks:<\/p>\n<ul>\n<li><a href=\"http:\/\/mvccontrib.codeplex.com\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">MvcContrib<\/a> bringing useful extension methods and strongly typed helpers to ASP.NET MVC<\/li>\n<li><a href=\"http:\/\/automapper.codeplex.com\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">AutoMapper<\/a> enabling easy mapping between the domain and the view models<\/li>\n<li><a href=\"http:\/\/fluentvalidation.codeplex.com\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">FluentValidation<\/a> &#8211; a small validation library for .NET that uses a fluent interface and lambda expressions for building validation rules for your business objects<\/li>\n<li><a href=\"http:\/\/community.jboss.org\/wiki\/NHibernateforNET\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">NHibernate<\/a> \u2013 a popular ORM in the .NET world<\/li>\n<li><a href=\"http:\/\/fluentnhibernate.org\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">FluentNHibernate<\/a> &#8211; a statically compiled alternative to NHibernate&rsquo;s standard hbm xml mapping<\/li>\n<li><a href=\"http:\/\/www.springframework.net\/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Spring.NET<\/a> \u2013 object container and dependency Injection framework for .NET<\/li>\n<li><a href=\"http:\/\/www.ayende.com\/projects\/rhino-mocks.aspx\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Rhino.Mocks<\/a> &#8211; A dynamic mock object framework for the .Net platform. It&rsquo;s purpose is to ease testing by allowing the developer to create mock implementations of custom objects and verify the interactions using unit testing<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>Armed with this arsenal of frameworks let\u2019s start exploring the solution structure. I\u2019ve opted for 2 projects solution but in many real world applications more levels of abstraction could be brought to the business layer. Personally I favor to have less big assemblies rather than many small assemblies into the solution. Fewer the assemblies, faster the load time and faster the IDE. In this case particular attention should be brought to bring proper separation of concerns inside the same assembly<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"http:\/\/bratched.com\/images\/stories\/project_structure.png\"><img decoding=\"async\" loading=\"lazy\" style=\"border: 0px currentColor;margin-right: 0px;margin-left: 0px\" title=\"ASP.Net MVC project structure\" src=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/importedmedia\/blogmedia-project-structure-thumb-png.png\" alt=\"ASP.Net MVC project structure\" width=\"196\" height=\"431\" align=\"left\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<\/p>\n<p>The domain consists of a single User class and a repository interface defining the different operations on this model:<\/p>\n<p><a href=\"http:\/\/bratched.com\/images\/stories\/Domain.png\"><img decoding=\"async\" loading=\"lazy\" style=\"border-width: 0px\" title=\"ASP.Net MVC Domain with Repository\" src=\"http:\/\/dev.bratched.fr\/fr\/wp-content\/uploads\/sites\/2\/importedmedia\/blogmedia-domain-thumb-png.png\" alt=\"ASP.Net MVC Domain with Repository\" width=\"244\" height=\"103\" border=\"0\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<h1>Mapping<\/h1>\n<p>The next step is to define the mapping between our domain and a relational model expressed in a fluent manner:<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:9526fe5b-b6d5-41ae-929d-5e41eff7a774\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>public class UserMap : ClassMap&lt;User&gt;\n{\n    public UserMap()\n    {\n        Table(\"users\");\n        Id(x =&gt; x.Id, \"usr_id\");\n        Map(x =&gt; x.FirstName, \"usr_firstname\");\n        Map(x =&gt; x.LastName, \"usr_lastname\");\n        Map(x =&gt; x.Age, \"usr_age\");\n    }\n}\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>And here\u2019s the implementation of the repository:<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:37462f8d-9fff-460d-a647-36cae11a0193\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>public class SqlUsersRepository : HibernateDaoSupport, IUsersRepository\n{\n   public IEnumerable&lt;User&gt; GetUsers()\n   {\n       return HibernateTemplate.LoadAll&lt;User&gt;();\n   }\n\n   public User Get(int id)\n   {\n       return HibernateTemplate.Get&lt;User&gt;(id);\n   }\n\n   public void Delete(int id)\n   {\n       HibernateTemplate.Delete(new User { Id = id });\n   }\n\n   public int Save(User user)\n   {\n       return (int)HibernateTemplate.Save(user);\n   }\n\n   public void Update(User user)\n   {\n       HibernateTemplate.Update(user);\n   }\n}<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p><em>HibernateDaoSupport<\/em> is a base class defined by the Spring Framework managing SQL transactions and NHibernate session.<\/p>\n<p>Once we have implemented the data access layer we could move on to the web part. The application consists of a single RESTful UsersController allowing the standard CRUD operations with our users model. As all our views are strongly typed we shall define a view model for each view and a mapping between the domain model and this view model. In our simple case the view model will simply have the same structure as the domain model but in real world scenarios it will be a projection of the domain model for a particular view.<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:d379d002-9b69-47af-8707-a672190190ac\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>[Validator(typeof(UserViewModelValidator))]\npublic class UserViewModel\n{\n     public int Id { get; set; }\n     public string FirstName { get; set; }\n     public string LastName { get; set; }\n     public int? Age { get; set; }\n}\n<\/pre>\n<\/div>\n<p>And the respective validator:<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:1560c438-cde5-400e-baad-7a06e010131a\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>public class UserViewModelValidator : AbstractValidator&lt;UserViewModel&gt;\n{\n    public UserViewModelValidator()\n    {\n        RuleFor(x =&gt; x.FirstName)\n            .NotEmpty()\n            .WithMessage(\"First name is required\")\n            .DisplayName(\"First name *\");\n\n        RuleFor(x =&gt; x.LastName)\n            .NotEmpty()\n            .WithMessage(\"Last name is required\")\n            .DisplayName(\"Last name *\");\n    }\n}\n<\/pre>\n<\/div>\n<p>And mapper between the domain and view model:<\/p>\n<p>&nbsp;<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:62385ce4-e269-40f5-9152-d22cbafa7d77\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>public class UserMapper : IMapper\n{\n    static UserMapper()\n    {\n        Mapper.CreateMap&lt;User, UserViewModel&gt;();\n        Mapper.CreateMap&lt;UserViewModel, User&gt;();\n    }\n\n    public object Map(object source, Type sourceType, Type destinationType)\n    {\n        return Mapper.Map(source, sourceType, destinationType);\n    }\n}\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>This bidirectional mapper will be used by our RESTful controller:<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:bafc9f0a-cb41-4155-a7c8-55ebafde97fa\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>public class UsersController : BaseController&lt;IUsersRepository&gt;\n{\n    public UsersController(IUsersRepository repository, IMapper userMapper) \n        : base(repository, userMapper)\n    { }\n\n    [AutoMap(typeof(IEnumerable&lt;User&gt;), typeof(IEnumerable&lt;UserViewModel&gt;))]\n    public ActionResult Index()\n    {\n        \/\/ return all users\n        var users = Repository.GetUsers();\n        return View(users);\n\n    }\n\n    public ActionResult New()\n    {\n        \/\/ return an HTML form for describing a new user\n        return View();\n    }\n\n    [HttpPost]\n    [AutoMap(typeof(User), typeof(UserViewModel))]\n    public ActionResult Create(UserViewModel userView)\n    {\n        \/\/ create a new user\n        if (!ModelState.IsValid)\n        {\n            return View(\"New\", userView);\n        }\n        var user = (User)ModelMapper.Map(userView, typeof(UserViewModel), typeof(User));\n        Repository.Save(user);\n        return RedirectToAction(\"Index\", \"Users\");\n    }\n\n    [AutoMap(typeof(User), typeof(UserViewModel))]\n    public ActionResult Show(int id)\n    {\n        \/\/ find and return a specific user\n        var user = Repository.Get(id);\n        return View(user);\n    }\n\n    [AutoMap(typeof(User), typeof(UserViewModel))]\n    public ActionResult Edit(int id)\n    {\n        \/\/ return an HTML form for editing a specific user\n        var user = Repository.Get(id);\n        return View(user);\n    }\n\n    [HttpPut]\n    public ActionResult Update(UserViewModel userView)\n    {\n        \/\/ find and update a specific user\n        if (!ModelState.IsValid)\n        {\n            return View(\"Edit\", userView);\n        }\n        var user = (User)ModelMapper.Map(userView, typeof(UserViewModel), typeof(User));\n        Repository.Update(user);\n        return RedirectToAction(\"Index\", \"Users\");\n    }\n\n    [HttpDelete]\n    public ActionResult Destroy(int id)\n    {\n        \/\/ delete a specific user\n        Repository.Delete(id);\n        return RedirectToAction(\"Index\", \"Users\");\n    }\n}<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Notice the <em>AutoMapAttribute<\/em>. This is a custom attribute allowing us to automatically convert the domain model retrieved by the repository to a view model and present it to the view:<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:3ce37fc1-3a31-4f20-9295-4733dd639082\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]\npublic class AutoMapAttribute : ActionFilterAttribute\n{\n    public Type SourceType { get; private set; }\n    public Type DestType { get; private set; }\n\n    public AutoMapAttribute(Type sourceType, Type destType)\n    {\n        SourceType = sourceType;\n        DestType = destType;\n    }\n\n    public override void OnActionExecuted(ActionExecutedContext filterContext)\n    {\n        base.OnActionExecuted(filterContext);\n        var controller = filterContext.Controller as IModelMapperController;\n        if (controller == null)\n        {\n            return;\n        }\n        var model = filterContext.Controller.ViewData.Model;\n        if (model != null &amp;&amp; SourceType.IsAssignableFrom(model.GetType()))\n        {\n            var viewModel = controller.ModelMapper.Map(model, SourceType, DestType);\n            filterContext.Controller.ViewData.Model = viewModel;\n        }\n    }\n}<\/pre>\n<\/div>\n<p>The <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.web.mvc.actionfilterattribute.onactionexecuted.aspx\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">OnActionExecuted<\/a> method will be called after each action method has finished executing and it will use the model passed to the view and convert it to the appropriate view model. It simply substitutes the <em>ViewData.Model<\/em> property with the appropriate view model to finally pass it to the view for rendering.<\/p>\n<p>&nbsp;<\/p>\n<p>The controller follows the standard RESTful conventions for naming the action and the HTTP verbs:<\/p>\n<p>&nbsp;<\/p>\n<table border=\"1\" width=\"863\" cellspacing=\"0\" cellpadding=\"2\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"133\">\n<p align=\"center\"><strong>URL<\/strong><\/p>\n<\/td>\n<td valign=\"top\" width=\"96\">\n<p align=\"center\"><strong>HTTP Verb<\/strong><\/p>\n<\/td>\n<td valign=\"top\" width=\"286\">\n<p align=\"center\"><strong>Action<\/strong><\/p>\n<\/td>\n<td valign=\"top\" width=\"346\">\n<p align=\"center\"><strong>Description<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"138\">\/users\/index<\/td>\n<td valign=\"top\" width=\"99\">GET<\/td>\n<td valign=\"top\" width=\"283\">Index()<\/td>\n<td valign=\"top\" width=\"346\">return all users<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"142\">\/users\/show\/id<\/td>\n<td valign=\"top\" width=\"101\">GET<\/td>\n<td valign=\"top\" width=\"281\">Show(int id)<\/td>\n<td valign=\"top\" width=\"346\">return a specific user<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"146\">\/users\/new<\/td>\n<td valign=\"top\" width=\"102\">GET<\/td>\n<td valign=\"top\" width=\"280\">New()<\/td>\n<td valign=\"top\" width=\"346\">return an HTML form for creating a new user<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"149\">\/users\/create<\/td>\n<td valign=\"top\" width=\"103\">POST<\/td>\n<td valign=\"top\" width=\"278\">Create(UserViewModel userView)<\/td>\n<td valign=\"top\" width=\"346\">create a new user<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"151\">\/users\/edit\/id<\/td>\n<td valign=\"top\" width=\"103\">GET<\/td>\n<td valign=\"top\" width=\"278\">Edit(int id)<\/td>\n<td valign=\"top\" width=\"346\">return an HTML form for editing a specific user<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"153\">\/users\/update<\/td>\n<td valign=\"top\" width=\"103\">PUT<\/td>\n<td valign=\"top\" width=\"277\">Update(UserViewModel userView)<\/td>\n<td valign=\"top\" width=\"346\">update a specific user<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"155\">\/users\/destroy\/id<\/td>\n<td valign=\"top\" width=\"103\">DELETE<\/td>\n<td valign=\"top\" width=\"276\">Destroy(int id)<\/td>\n<td valign=\"top\" width=\"346\">delete a specific user<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>&nbsp;<\/p>\n<p>Because most browsers support submitting HTML forms only using the GET and POST verbs, there\u2019s the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ee407388.aspx\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Html.HttpMethodOverride<\/a> helper which generates a hidden field inside the form and is used by the routing engine to dispatch to the proper controller action.<\/p>\n<h1>Unit Tests<\/h1>\n<p>Unit testing our controller actions is essential. I\u2019ve been using the excellent <a href=\"http:\/\/mvccontrib.codeplex.com\/wikipage?title=TestHelper\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">MVCContrib.TestHelper<\/a> in conjunction with the <a href=\"http:\/\/www.ayende.com\/projects\/rhino-mocks.aspx\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Rhino.Mocks<\/a> framework to test controllers in isolation by mocking the HTTP context. Here\u2019s how the test logic looks like:<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:57c3e7e1-0043-4afa-8eee-8fd1fee961bc\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>[TestClass]\npublic class UsersControllerTests : TestControllerBuilder\n{\n    private UsersController _sut;\n    private IUsersRepository _repositoryStub;\n    private IMapper _userMapperStub;\n\n    public UsersControllerTests()\n    {\n    }\n\n    private TestContext testContextInstance;\n\n    \/\/\/ \u00a0\n    \/\/\/Gets or sets the test context which provides\n    \/\/\/information about and functionality for the current test run.\n    \/\/\/\u00a0\n    public TestContext TestContext\n    {\n        get\n        {\n            return testContextInstance;\n        }\n        set\n        {\n            testContextInstance = value;\n        }\n    }\n\n    \/\/ Use TestInitialize to run code before running each test \n    [TestInitialize()]\n    public void MyTestInitialize() \n    {\n        _repositoryStub = MockRepository.GenerateStub&lt;IUsersRepository&gt;();\n        _userMapperStub = MockRepository.GenerateStub&lt;IMapper&gt;();\n        _sut = new UsersController(_repositoryStub, _userMapperStub);\n        InitializeController(_sut);\n    }\n\n    [TestMethod]\n    public void UsersController_Index()\n    {\n        \/\/ arrange\n        var users = new User[0];\n        _repositoryStub.Stub(x =&gt; x.GetUsers()).Return(users);\n\n        \/\/ act\n        var actual = _sut.Index();\n\n        \/\/ assert\n        actual\n            .AssertViewRendered()\n            .WithViewData&lt;User[]&gt;()\n            .ShouldBe(users);\n    }\n\n    [TestMethod]\n    public void UsersController_New()\n    {\n        \/\/ act\n        var actual = _sut.New();\n\n        \/\/ assert\n        actual\n            .AssertViewRendered();\n    }\n\n    [TestMethod]\n    public void UsersController_Create_Invalid_Model_State()\n    {\n        \/\/ arrange\n        _sut.ModelState.AddModelError(\"FirstName\", \"First name is required\");\n        var userView = new UserViewModel();\n\n        \/\/ act\n        var actual = _sut.Create(userView);\n\n        \/\/ assert\n        actual\n            .AssertViewRendered()\n            .ForView(\"New\")\n            .WithViewData&lt;UserViewModel&gt;()\n            .ShouldBe(userView);\n    }\n\n    [TestMethod]\n    public void UsersController_Create_Success()\n    {\n        \/\/ arrange\n        var userView = new UserViewModel();\n        var user = new User();\n        _userMapperStub\n            .Stub(x =&gt; x.Map(userView, typeof(UserViewModel), typeof(User)))\n            .Return(user);\n\n        \/\/ act\n        var actual = _sut.Create(userView);\n\n        \/\/ assert\n        actual\n            .AssertActionRedirect()\n            .ToAction&lt;UsersController&gt;(c =&gt; c.Index());\n        _repositoryStub.AssertWasCalled(x =&gt; x.Save(user));\n    }\n\n    [TestMethod]\n    public void UsersController_Show()\n    {\n        \/\/ arrange\n        var id = 1;\n        var user = new User();\n        _repositoryStub.Stub(x =&gt; x.Get(id)).Return(user);\n\n        \/\/ act\n        var actual = _sut.Show(id);\n\n        \/\/ assert\n        actual\n            .AssertViewRendered()\n            .WithViewData&lt;User&gt;()\n            .ShouldBe(user);\n    }\n\n    [TestMethod]\n    public void UsersController_Edit()\n    {\n        \/\/ arrange\n        var id = 1;\n        var user = new User();\n        _repositoryStub.Stub(x =&gt; x.Get(id)).Return(user);\n\n        \/\/ act\n        var actual = _sut.Edit(id);\n\n        \/\/ assert\n        actual\n            .AssertViewRendered()\n            .WithViewData&lt;User&gt;()\n            .ShouldBe(user);\n    }\n\n    [TestMethod]\n    public void UsersController_Update_Invalid_Model_State()\n    {\n        \/\/ arrange\n        _sut.ModelState.AddModelError(\"FirstName\", \"First name is required\");\n        var userView = new UserViewModel();\n\n        \/\/ act\n        var actual = _sut.Update(userView);\n\n        \/\/ assert\n        actual\n            .AssertViewRendered()\n            .ForView(\"Edit\")\n            .WithViewData&lt;UserViewModel&gt;()\n            .ShouldBe(userView);\n    }\n\n    [TestMethod]\n    public void UsersController_Update_Success()\n    {\n        \/\/ arrange\n        var userView = new UserViewModel();\n        var user = new User();\n        _userMapperStub\n            .Stub(x =&gt; x.Map(userView, typeof(UserViewModel), typeof(User)))\n            .Return(user);\n\n        \/\/ act\n        var actual = _sut.Update(userView);\n\n        \/\/ assert\n        actual\n            .AssertActionRedirect()\n            .ToAction&lt;UsersController&gt;(c =&gt; c.Index());\n        _repositoryStub.AssertWasCalled(x =&gt; x.Update(user));\n    }\n\n    [TestMethod]\n    public void UsersController_Destroy()\n    {\n        \/\/ arrange\n        var id = 1;\n\n        \/\/ act\n        var actual = _sut.Destroy(id);\n\n        \/\/ assert\n        actual\n            .AssertActionRedirect()\n            .ToAction&lt;UsersController&gt;(c =&gt; c.Index());\n        _repositoryStub.AssertWasCalled(x =&gt; x.Delete(id));\n    }\n}\n\n<\/pre>\n<p>&nbsp;<\/p>\n<h1><span style=\"color: #000000\">Views<\/span><\/h1>\n<\/div>\n<p>And the last but not least part of the picture are the views:<\/p>\n<h2>Index.aspx<\/h2>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:649bb274-6f40-4fa0-b2fb-a1244a0f1076\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>&lt;%@ Page Title=\"\" Language=\"C#\" MasterPageFile=\"~\/Views\/Shared\/Site.Master\" Inherits=\"System.Web.Mvc.ViewPage\" %&gt;\n&lt;%@ Import Namespace=\"SampleMvc.Web.Models\" %&gt;\n&lt;%@ Import Namespace=\"SampleMvc.Web.Controllers\" %&gt;\n\n&lt;asp:Content ID=\"Content1\" ContentPlaceHolderID=\"TitleContent\" runat=\"server\"&gt;\n    Index\n&lt;\/asp:Content&gt;\n\n&lt;asp:Content ID=\"Content2\" ContentPlaceHolderID=\"MainContent\" runat=\"server\"&gt;\n    &lt;h2&gt;Index&lt;\/h2&gt;\n   \u00a0&lt;%: Html.Grid&gt;(Model)\n        .Columns(column =&gt; {\n            column.For(\"TableLinks\").Named(\"\");\n            column.For(model =&gt; model.FirstName);\n            column.For(model =&gt; model.LastName);\n            column.For(model =&gt; model.Age);\n        })\n    %&gt;\n    &lt;p&gt;\n        &lt;%: Html.ActionLink&gt;(c =&gt; c.New(), \"Create New\") %&gt;\n    &lt;\/p&gt;\u00a0\n&lt;\/asp:Content&gt;<\/pre>\n<\/div>\n<h2>TableLinks.ascx<\/h2>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:0ebe3179-8ba3-48d8-b5f2-e1af8c248169\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>&lt;%@ Control Language=\"C#\" Inherits=\"System.Web.Mvc.ViewUserControl\" %&gt;\n&lt;%@ Import Namespace=\"SampleMvc.Web.Controllers\" %&gt;\n&lt;td&gt;\n    &lt;%: Html.ActionLink&gt;(c =&gt; c.Edit(Model.Id), \"Edit\") %&gt; |\n    &lt;%: Html.ActionLink&gt;(c =&gt; c.Show(Model.Id), \"Details\") %&gt; |\n    &lt;% using (Html.BeginForm&gt;(c =&gt; c.Destroy(Model.Id))) { %&gt;\n        &lt;%: Html.HttpMethodOverride(HttpVerbs.Delete) %&gt;\n        &lt;input type=\"submit\" value=\"Delete\" \/&gt;\n    &lt;% } %&gt;\n&lt;\/td&gt;\n<\/pre>\n<\/div>\n<h2>Edit.aspx<\/h2>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:e361d11f-3cfe-4886-8be5-2cf2029737f5\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>&lt;%@ Page Title=\"\" Language=\"C#\" MasterPageFile=\"~\/Views\/Shared\/Site.Master\" Inherits=\"System.Web.Mvc.ViewPage\" %&gt;\n&lt;%@ Import Namespace=\"SampleMvc.Web.Controllers\" %&gt;\n\n&lt;asp:Content ID=\"Content1\" ContentPlaceHolderID=\"TitleContent\" runat=\"server\"&gt;\n    Edit\n&lt;\/asp:Content&gt;\u00a0\n\n&lt;asp:Content ID=\"Content2\" ContentPlaceHolderID=\"MainContent\" runat=\"server\"&gt;\n    &lt;h2&gt;Edit&lt;\/h2&gt;\u00a0\u00a0\n    &lt;% using (Html.BeginForm&gt;(c =&gt; c.Update(null))) {%&gt;\n        &lt;%: Html.ValidationSummary(true) %&gt;\n        &lt;%: Html.HttpMethodOverride(HttpVerbs.Put) %&gt;\n        &lt;%: Html.HiddenFor(model =&gt; model.Id) %&gt;\n        &lt;%: Html.EditorForModel() %&gt;\n        &lt;p&gt;\n            &lt;input type=\"submit\" value=\"Save\" \/&gt;\n        &lt;\/p&gt;\n\u00a0    &lt;% } %&gt;\n    &lt;div&gt;\n        &lt;%: Html.ActionLink&gt;(c =&gt; c.Index(), \"Back to List\") %&gt;\n    &lt;\/div&gt;\u00a0\n &lt;\/asp:Content&gt;<\/pre>\n<\/div>\n<h2>New.aspx<\/h2>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:3365807c-d5f9-4c1e-9c2c-8bd3cbdbbb77\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>&lt;%@ Page Title=\"\" Language=\"C#\" MasterPageFile=\"~\/Views\/Shared\/Site.Master\" Inherits=\"System.Web.Mvc.ViewPage\" %&gt;\n&lt;%@ Import Namespace=\"SampleMvc.Web.Controllers\" %&gt;\n\n&lt;asp:Content ID=\"Content1\" ContentPlaceHolderID=\"TitleContent\" runat=\"server\"&gt;\n    New\n&lt;\/asp:Content&gt;\u00a0\n\n&lt;asp:Content ID=\"Content2\" ContentPlaceHolderID=\"MainContent\" runat=\"server\"&gt;\n    &lt;h2&gt;New&lt;\/h2&gt;\n    &lt;% using (Html.BeginForm&gt;(c =&gt; c.Create(null))) {%&gt;\n        &lt;%: Html.ValidationSummary(true) %&gt;\n\n        &lt;%: Html.EditorForModel() %&gt;\n        &lt;p&gt;\n            &lt;input type=\"submit\" value=\"Create\" \/&gt;\n        &lt;\/p&gt;\n    &lt;% } %&gt;\n    &lt;div&gt;\n        &lt;%: Html.ActionLink&gt;(c =&gt; c.Index(), \"Back to List\") %&gt;\n    &lt;\/div&gt;\n&lt;\/asp:Content&gt;<\/pre>\n<\/div>\n<h2>UserViewModel.ascx editor template<\/h2>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:ac712efb-8ef5-4743-87ef-7334eb58bbb3\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre>&lt;%@ Control Language=\"C#\" Inherits=\"System.Web.Mvc.ViewUserControl\" %&gt;\n\n&lt;fieldset&gt;\n    &lt;legend&gt;Fields&lt;\/legend\u00a0&gt;\n    &lt;div class=\"editor-label\"&gt;\n        &lt;%: Html.LabelFor(model =&gt; model.FirstName) %&gt;\n    \u00a0&lt;\/div&gt;\n\u00a0   &lt;div class=\"editor-field\"&gt;\n        &lt;%: Html.TextBoxFor(model =&gt; model.FirstName) %&gt;\n        &lt;%: Html.ValidationMessageFor(model =&gt; model.FirstName) %&gt;\n    \u00a0&lt;\/div&gt;            \n    &lt;div class=\"editor-label\"&gt;\n        &lt;%: Html.LabelFor(model =&gt; model.LastName) %&gt;\u00a0\u00a0\n   \u00a0&lt;\/div&gt;\n\u00a0   &lt;div class=\"editor-field\"&gt;\n        &lt;%: Html.TextBoxFor(model =&gt; model.LastName) %&gt;\n        &lt;%: Html.ValidationMessageFor(model =&gt; model.LastName) %&gt;\n   \u00a0\u00a0&lt;\/div&gt;            \n    &lt;div class=\"editor-label\"&gt;\n        &lt;%: Html.LabelFor(model =&gt; model.Age) %&gt;\n    \u00a0&lt;\/div&gt;\n    \u00a0&lt;div class=\"editor-field\"&gt;\n        &lt;%: Html.TextBoxFor(model =&gt; model.Age) %&gt;\n        &lt;%: Html.ValidationMessageFor(model =&gt; model.Age) %&gt;\n    \u00a0&lt;\/div&gt;\n\u00a0&lt;\/fieldset&gt;<\/pre>\n<\/div>\n<p>A very important aspect of the views is that they are all strongly typed to a view model and use only strongly typed helpers, even for generating the links. One day when Visual Studio becomes power enough you will be able to seamlessly refactor\/rename a property without worrying about all those <a href=\"http:\/\/en.wikipedia.org\/wiki\/Magic_string_(programming)\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">magic strings<\/a>.<\/p>\n<p>&nbsp;<\/p>\n<p>Further enchantments will include adding client side validation using the jQuery validate plugin in order to improve the user experience and preserve bandwidth.<\/p>\n<div class=\"clearfix\"><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Sample MVC Solution In this post I will show a sample ASP.NET MVC 2.0 project structure illustrating different concepts such as data access, user input validation and mapping between the domain and the view model. The project is still under construction but the source code is available at github. I will illustrate the usage of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[25],"tags":[26,29,30,31,32,33,5],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts\/546"}],"collection":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/comments?post=546"}],"version-history":[{"count":0,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/posts\/546\/revisions"}],"wp:attachment":[{"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/media?parent=546"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/categories?post=546"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bratched.com\/fr\/wp-json\/wp\/v2\/tags?post=546"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}