Introduction
As the name implies, it's a project starter template for ASP.NET MVC based solutions (mainly for multi-paged enteprise solutions, which could change in the future if there will be need for it).
This project was born after working on maintaining and improving few MVC projects and seeing how much of a headache these projects were bringing with them. All because of a bad project foundation, which in the long run started to ruin program's design and maintainability even more and more. Even though there are a lot of examples of good ASP.NET MVC practises on the web, collecting them all and applying in the first applications is not an easy task.
Background
There are few rules this project will try to follow:
- Simplicity - a good indicator of any program is it's simplicity thus resulting in lack of code. Any new ideas or findings on making code simplier to understand and use will be incorporated first. Hard to understand code should be reported to the issue police at GitHub along with reasoning on why it was hard to understand and use (suggestions are also welcomed in the comments).
- Activity - by now there are a lot of good projects which are no longer actively developed (dead). And it is very sad. MVC.Template will try to be in the loop for years to come.
- Open - this project will always be open sourced with MIT license. Use it, like it, hate it, do whatever.
- Up to date - any new releases of frameworks used in the project will always be updated to their newest releases. So even though we all love MVC5, it will not be supported after vNext release will be sufficient to replace it and so on.
Features
- Model-View-ViewModel architectural design.
- Lowercase or normal ASP.NET urls.
- Custom membership providers.
- Protection from CSRF, XSS, etc.
- Easy project renaming.
- Dependency injection.
- Custom error pages.
- Globalization.
- Audit log.
- Site map.
- Tests.
Project structure
- MvcTemplate - is used for keeping any project specific implementation, which can not reside in other projects. Mainly it will be custom project components, which can not be easily reused because of hard dependencies to other solution projects, like data access layer.
- MvcTemplate.Components - should only contain reusable components. It means no references to any MvcTemplate.Xxxxx project, with an exception of MvcTemplate.Resources. This assembly is intented to keep all the reusable "goodies" which are born while implementing a specific project. So that it can be reused in the next projects and save us some more time for programming more interesting code.
- MvcTemplate.Controllers - separates controllers and their routing configuration from other assemblies. It's known that separation of controllers has some disadvantages (like not being able to generate strongly typed helpers with T4MVC). But until someone proves this approach wrong, controllers will be separated.
- MvcTemplate.Data - hides data access layer implementation from other assemblies. So that assemblies which are using data access layer would not have to reference frameworks like EntityFramework. Or, know implementation details about domain model mappings with view models.
- MvcTemplate.Objects - contains all domain and view model classes in one place. Any other classes should not be here.
- MvcTemplate.Resources - keeps all our "beloved magic strings" in healthy environment. It is also used for easy solution globalization.
- MvcTemplate.Services - is the assembly which does the main job. And by the main job I mean managing program state, like creating, editing, deleting domain entities; sending emails; eating CPU cycles and what not. This is where the processing logic should be, not in the controller. Controllers should only act as a brainless routing switch based on services and validation outputs.
- MvcTemplate.Tests - yet another main reason why MVC applications become as bad as they are. One of the main problem MVC architecture is trying to address is testability, and not testing MVC application should be considered a sin. This assembly covers most (~99%) of the solution asemblies code base. Mainly through unit testing.
- MvcTemplate.Validators - separates all domain validation logic from controllers and their services. So it can be reused in other controllers if needed (e.g. account registration and profile update shares same validation concepts).
- MvcTemplate.Web - and finally web assembly, which is kind of empty. Because it mainly consists of UI representation views, styling sheets, scripts and other client side components.
Installation
Using the code
Models
As you might know in traditional MVC, models are the ones to hold all business logic. But in this template they are just POCOs for representing database tables. The same applies to representation models also known as view models.
public class Account : BaseModel
{
[Required]
[StringLength(128)]
[Index(IsUnique = true)]
public String Username { get; set; }
[Required]
public Boolean IsAdmin { get; set; }
}
public class AccountView : BaseView
{
[Required]
[StringLength(128)]
[Index(IsUnique = true)]
public String Username { get; set; }
}
Database
Registering newly created model with the database is relatively easy. First of all, new DbSet has to be added to the main Context class:
public class Context : DBContext
{
...
protected DbSet<Account> Accounts { get; set; }
...
}
MVC.Template is using code first approach for database management, so after adding new DbSet to the context you will need to run "Add-Migration <your_migration_name>", followed by "update-database" command in "Package Manager Console".
Controllers
Any new controller should inherit BaseController, which can be used as an extension point for every controller. Also it keeps shared controller methods, like authorization and redirection to static pages (e.g. "404 not found", "403 unauthorized"). Other controller bases include:
- ServicedController - for controllers with injected service instance,
- ValidatedController - for controllers with injected service and validator instances.
The one to choose should be obvious from your needs in a controller. All these controller bases are under [AuthorizationFilter] attribute, so they will all be authorized by default.
public class AccountsController : ValidatedController<IAccountValidator, IAccountService>
{
...
[HttpGet]
public ActionResult Edit(String id)
{
return NotEmptyView(Service.Get<AccountView>(id));
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(AccountView account)
{
if (!Validator.CanEdit(account))
return View(account);
Service.Edit(account);
return RedirectIfAuthorized("Index");
}
...
}
Services
Services are ment to keep business logic for changing domain model or doing other business tasks. One service class should only "serve" one domain model, thus the name of the service should generally be <Domain model name>Service (e.g. AccountService, RoleService). In addition to that, every service should have an interface which inherits IService.
public interface IAccountService : IService
{
...
TView Get<TView>(String id) where TView : BaseView;
void Edit(AccountView view);
...
}
public AccountService : IAccountService
{
...
public TView Get<TView>(String id) where TView : BaseView
{
return UnitOfWork.GetAs<Account, TView>(id);
}
public void Edit(AcountView view)
{
Account account = UnitOfWork.To<Account>(view);
UnitOfWork.Update(account);
UnitOfWork.Commit();
}
...
}
Validators
Validators, unlike services are ment to hold all business validation logic for domain models. Validator implementation follow the same pattern as services. Validation class for one domain model with validator interface.
public interface IAccountValidator : IValidator
{
Boolean CanEdit(AccountView view);
}
public AccountValidator : IAccountValidator
{
...
public Boolean CanEdit(AcountView view)
{
Boolean isValid = IsUniqueUsername(view);
isValid &= ModelState.IsValid;
return isValid;
}
...
}
Views
Views should always be written for view models like "AccountView", "RoleView", but never for domain models.
@model AccountEditView
<div class="col-xs-12">
<div class="widget-box">
<div class="widget-header">
<span class="widget-header-icon fa fa-th-list"></span>
<h5>@Headers.AdministrationAccounts</h5>
<div class="widget-header-buttons">
@if (Html.IsAuthorizedFor("Details"))
{
<a class="btn" href="@Url.Action("Details", new { id = Model.Id })">
<i class="fa fa-info"></i><span class="text">@Actions.Details</span>
</a>
}
</div>
</div>
<div class="widget-content">
@using (Html.BeginForm())
{
@Html.AntiForgeryToken() @* Validating anti forgery token on all post actions. *@
<div class="form-group">
<div class="control-label col-xs-12 col-md-3 col-lg-2">
@Html.FormLabelFor(model => model.Username) @* Custom label helper to add required '*' spans and localizing labels. *@
</div>
<div class="control-content col-xs-12 col-md-9 col-lg-5">
@Html.FormTextBoxFor(model => model.Username) @* Custom text box helper to add necessary attributes, like readonly on not editable fields. *@
</div>
<div class="control-validation col-xs-12 col-lg-5">
@Html.ValidationMessageFor(model => model.Username)
</div>
</div>
<div class="form-group">
<div class="form-actions col-xs-12 col-lg-7">
<input class="btn btn-primary" type="submit" value="@Actions.Submit" />
</div>
</div>
}
</div>
</div>
</div>
Globalization
Currently default language is English, in addition to that Lithuanian was added, just for an example of multiple languages with different number and date formats in the system. You can easily disable Lithuanian language by removing it from the Languages.config file (and then you have time, removing it's resources or replacing them with your language of choice).
="1.0" ="utf-8"
<languages>
<language name="English" culture="en-GB" abbrevation="en" default="true" />
</languages>
Leaving only one language will remove any language selections in the system automatically.
Summary
In this article, I introduced you to MVC.Template, a starting project template for ASP.NET MVC based solutions. And explained starting project structure, it's installation and basic code usage. More examples can always be found in MVC.Template's codebase at GitHub. This project will be actively develop by me and my colleagues at work. And hopefully become ASP.NET MVC project starting point for new and experienced developers alike.
History
2017.01.25 Updates, accordingly with v1.7.
2016.05.07 Updates, accordingly with v1.6.
2015.10.26 Updates, accordingly with v1.5.
2015.07.06 Updates, accordingly with v1.4.
2015.04.12 Updates, accordingly with v1.3.
2015.01.05 Updates, accordingly with v1.2.
2014.11.09 Updates, accordingly with v1.1.
2014.09.22 Initial article for v1.0.