Welcome to Asp.Net MVC 5 Starter Kit’s documentation!

Asp.Net MVC 5 Starter Kit is a basic, clean, globalized and S.O.L.I.D template with all the necessary boilerplate is ready to go.

Attention: Docs are still a work in progress

Source Code:

Contents:

Getting Started

Get the project up and running with a few simple steps.

Minimum Requirements

SQL Server Express LocalDB is included in Visual Studio 2017 editions.

Running locally

Default credentials for the initial user:

  • Username: admin
  • Password: password

Using Script (Recommended)

  • Open a new cmd or powershell console window.
  • Navigate to the project root folder (where it was extracted or cloned).
  • Execute the following command to setup the app: app install or ./app install.
    • The command above will perform the the following tasks:
      • Check for compatible PowerShell and .NET Framework versions.
      • Restore both server and client side dependent libraries (from Nuget and LibMan).
      • Compile the application after all required dependencies are properly restored.
      • Create the starterTemplateMVC5 database on your LocalDB instance.
  • If necessary, change the web.config connection string to point to your desired SQL Server edition.
  • Open the starterTemplateMVC5.sln solution file under the sources folder.
  • If necessary, set App.UI.Mvc5 as the startup project.
  • Hit F5 to start the application.

Manual Setup (if the above fails for some reason)

  • Create a database named starterTemplateMVC5
  • Execute the script under /sources/Data.Tools.Migrator/SqlServerScripts/Create_Initial_Db_Structure.sql to create the db objects.
  • Execute the script under /sources/Data.Tools.Migrator/SqlServerScripts/Create_Initial_SuperUser_Account.sql to create the default user.
  • If necessary, change the web.config connection string to point to your SQL Server.
  • Open the starterTemplateMVC5.sln solution file under the sources folder.
  • If necessary, set App.UI.Mvc5 as the startup project.
  • Restore required Nuget and LibMan dependencies.
  • Compile and you should be good to go.
  • Hit F5 to start the application.

Optional Settings

The configuration bellow will ensure your project will always start in the home page and that the II Express instance will not shutdown after stopping the debugger.

_images/mvc_suggested_settings.png

Solution .editorconfig

Under the 0.Solution Items solution folder, there is a global .editorconfig file that will ensure consistency between certain aspects of the code. This will apply to all users and will override their indivisual settings for this project.

More details can be found here.

Solution Details

Detailed information about the solution architecture, conventions, design choices and used tech.

Projects

Solution Folder / Project Description
0.Solution Items This folder contains globally available scripts, docs and configuration files.
1.Shared Libs This folder contains projects that are independent from the main model and could be event converted to nuget packages to be used in another projects. These projects provide several functionality such as email and sms messaging, storage, image processing and helful extensions.
2.Domain This folder contains the projects that can be considered the core of the onion architecture concept. This will contain the main code for the problem your project is solving.
3.Data This folder contains the projects that will handle the data your application will produce and/or consume.
App.Identity This is the project that will handle the ASP.Net Identity functionality for the users. It should be self contained and has its own repository and services to handle the app identities.
App.UI.Mvc5 This is the main User Interface project and will be the project that your end users will do most of their interaction with.

Architecture

The solution was built following the Onion Architecture concept.

The overall philosophy of the Onion Architecture is to keep your business logic and model in the middle (Core) of your application and push your dependencies as far outward as possible.

Basically, it states that code from inner layers should not depend on code from outer layers. It is very simple and help keeping things organized.

Check the image below:


_images/onion_architecture.png

Using the diagram above as an example, we can say that all code in the application can depend on code from the core layer, but code in the Infra layer can not depend on code from Services or User Interface layers.

For more information regarding this topic, please check the original definition post at http://jeffreypalermo.com/blog/the-onion-architecture-part-1/.

Folder Conventions

The application folders use a simple structure that has been proven to keep things handy and organized. Take the snippet bellow:

folder
├── subfolder
|   └── file.ext
└── file.ext

Every major feature has its own folder. Files shared by that feature will be kept in the root folder and possible subfolders will follow the same rules. Taking part of the Infrastructure folder from the App.UI.Mvc5 project, we have the following result:

Infrastructure
├── Blobs
│   ├── BlobService.cs
│   ├── BlobServiceConfigs.cs
│   ├── BlobUploadResult.cs
│   └── IBlobService.cs
├── Cookies
│   ├── GetCookie.cs
│   └── SetCookie.cs
├── Realtime
|   ├── Configs
│   |   ├── HubActivator.cs
│   |   ├── SignalRCamelCaseJsonResolver.cs
│   |   └── UserIdProvider.cs
|   ├── Hubs
│   |   └── DatabusHub.cs
│   ├── IRealtimeService.cs
│   └── RealtimeService.cs
├── UrlExtensions
│   ├── BlobThumbnail.cs
│   └── GetHomeUrl.cs
├── AppAreas.cs
├── AppJsonResult.cs
├── AppJsonResult.cs
├── AppViewEngine.cs
├── AppWebViewPage.cs
├── AuthorizeAttribute.cs
├── DirectRouteProvider.cs
└── SetLayout.cs

Namespace Conventions

Namespaces are defined keeping the folder structure above in mind, but they will go no farther then the first subfolder level for an specific project. Using the App.UI.Mvc5 project as example, the namespaces will be as follows:

App.UI.Mvc5
├── Controllers
│   └── (Namespace: App.UI.Mvc5.Controllers)
├── Infrastructure
|   ├── Configs
│   |   └── (Namespace: App.UI.Mvc5.Infrastructure)
│   └── (Namespace: App.UI.Mvc5.Infrastructure)
├── Models
│   └── (Namespace: App.UI.Mvc5.Models)
└── (Namespace: App.UI.Mvc5)

Tech Used and Third-Party Libraries

A list with the main tech and libraries that are used throughout the solution, for further information.

Microsoft

Third party libraries

* Free and paid version available.

Client Side Scritps

This solution does not use nuget packages to reference client side scripts and libraries. Instead, we use the newly available Library Manager (LibMan) that has become available since the release of Visual Studio 2017 Community v15.8.3+ or higher.

Referencing new libraries is pretty simple, just edit the libman.json file under the App.UI.Mvc5 project root providing the library name and the destination to where it should be restored and you’ll be good to go:

{
    "version": "1.0",
    "defaultProvider": "cdnjs",
    "libraries": [
        {
            "library": "jquery@3.3.1",
            "destination": "Assets/vendor/jquery"
        }
    ]
}

You can browse available libraries from the https://cdnjs.com/ catalog or add your preferred source.

For more details, check the official docs at https://docs.microsoft.com/en-us/aspnet/core/client-side/libman.

Asp.NET MVC Areas

It is not very common to see areas being used in MVC applications, but when properly configured, they can be very handy. Providing good modularity options to the solution:

_images/areas_top.png

Each area can be considered a ‘mini mvc’ website inside the main app and need just a few adjustments to get up and running:

  • Area name and namespaces
  • Area controllers
  • Views and models
  • Client side scripts

The easiest way to create a new area is copying the Blank area and renaming the required classes as needed.

Areas must have their own base controller that inherits from the main base controller (__BaseController.cs). For convention, the area base controller is named __AreaBaseController.cs.

Considering you may want to create a new area named Products, your new area base controller will look like the following:

using App.UI.Mvc5.Controllers;
using System.Web.Mvc;

namespace App.UI.Mvc5.Areas.Products.Controllers
{
    [RouteArea("Products", AreaPrefix = "products")]
    public abstract class __AreaBaseController : __BaseController
    {
    }
}

Notice above that we also set the AreaPrefix in there to make sure all routes that bellong to the new area will start with procucts/.

Another controller that is required is the _LandingController.cs. The sole responsibility for this controller is to redirect the request to the primary controller of the area. It was designed like this so all areas can be called in a standardized way. The landing controller for the example above will look like the following:

using System.Web.Mvc;

namespace App.UI.Mvc5.Areas.Products.Controllers
{
    [RoutePrefix("")]
    public class _LandingController : __AreaBaseController
    {
        [HttpGet]
        [Route(Name = "Products_Landing_Index_Get")]
        public ActionResult Index() => RedirectToAction("Index", "Overview");
    }
}

With that setup, it means that each time some request is made to the route .../products, the request will be redirected to the Index action in the OverviewController.cs class:

using App.UI.Mvc5.Infrastructure;
using App.UI.Mvc5.Models;
using System.Web.Mvc;

namespace App.UI.Mvc5.Areas.Products.Controllers
{
    [RoutePrefix("overview")]
    [TrackMenuItem("products.overview")]
    public partial class OverviewController : __AreaBaseController
    {
        [Route(Name = "Products_Overview_Index_Get")]
        public ActionResult Index()
        {
            var model = new EmptyViewModel();

            return View(model);
        }
    }
}

And for the final required step when setting up a new area, we must create a _MenuController.cs class as follows:

using App.UI.Mvc5.Models;
using System.Web.Mvc;

namespace App.UI.Mvc5.Areas.Products.Controllers
{
    [RoutePrefix("menu")]
    public class _MenuController : __AreaBaseController
    {
        [Route("top-menu-item", Name = "Products_Menu_TopMenuItem")]
        public ActionResult TopMenuItem()
        {
            var model = new EmptyPartialViewModel();

            return PartialView(model);
        }
    }
}

Notice that it will return a partial view named TopMenuItem.cshtml that will be available under the area Views folder:

@using App.UI.Mvc5.Areas.Products

<!-- Page Contents -->

<li class="nav-item @Menu.IfActiveItem("products.*", "active")">
    <a class="nav-link" href="@Url.Action("Index", "_Landing")">@(GetLocalizedString<AreaResources>("Products"))</a>
</li>

The partial view can then be called anywhere in the main website to render the area menu entry (normally in the root TopMenu.cshtml file):

<ul class="nav navbar-nav mr-auto">

    <li class="nav-item @Menu.IfActiveItem("root.landing", "active", string.Empty)">
        <a class="nav-link" href="@Url.GetHomeUrl()">@GetLocalizedString("Home")</a>
    </li>

    @Html.Action("TopMenuItem", "_Menu", new { area = AppAreas.GetAreaName(Area.Features) })

    @Html.Action("TopMenuItem", "_Menu", new { area = AppAreas.GetAreaName(Area.Blank) })

    @Html.Action("TopMenuItem", "_Menu", new { area = AppAreas.GetAreaName(Area.Products) })

</ul>

One last thing to notice is that, when using areas, all website links MUST know to which area the route is supposed to belong. To facilitate that process, the system provide a helper class named AppAreas.cs that can be found under the Infrastructure folder. Just add the new area name to the Areas enumerator and, when creating links, call the method as show above.

namespace App.UI.Mvc5.Infrastructure
{
    public enum Area : int
    {
        Root,
        Blank,
        Features,
        Management,
        Users,
        Products
    }

    public class AppAreas
    {
        public static string GetAreaName(Area area)
        {
            if (area == Area.Root)
            {
                return string.Empty;
            }

            return area.ToString();
        }
    }
}

Troubleshooting

Possible problems that may occur during the development lifecycle.

Scripted Build Errors

In some specific cases, the command app.cmd install may fail. If you’re facing problems with that, try the following steps:

  • On your development machine, look for the Visual Studio Installer application:

    _images/vs_installer.png

  • Make sure that you have the Nuget targets and build tasks selected:

    _images/vs_build_tools.png

    _images/vs_build_tools_targets.png

Assembly mappings

Specially after updating nuget packages, you may experience runtime errors like the following:

Could not load file or assembly 'xxx' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

That is usually caused by assembly bindings that were not properly updated, along with the packages, in your web.config file, under the configuration/runtime/assemblyBinding node:

_images/assembly_bindings.png

You can manually fix that by comparing the package versions with the ones being redirected to or let Visual Studio handle it for you by doing the following steps:

  • Completely delete the assemblyBinding node from the web.config file.

    _images/assembly_bindings_delete.png

  • Recompile the application using Visual Studio.

  • Click on the compilation warning as instructed and click ‘Yes’ to the action box that is shown.

    _images/assembly_bindings_warning.png

Your assembly bindings should now be pointing to the correct versions and the runtime errors will be gone.

For more details, check the official docs at https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/redirect-assembly-versions.