Creating an MVC Widget Showcase Site with Kentico EMS and MVC – Architecture Updates

By Bryan Soltis in MVC
·6 min read

We’ve made it to Part 4 in my Creating a new MVC site with Kentico 12 series. We started with the world’s most boring site and added some basic content. Next, we added a snazzy design and the new Page Builder functionality, and then some custom widgets and editors. In this final article, I’m going to take our architecture to the next level. So, let’s get started!

TL;DR Version

  • Dependency Injection simplifies your application and is at the core of MVC development.

  • All DI libraries do essentially the same thing. Choose one that fits your skills/architecture best.

  • ViewModels simplify your models and controllers. You can manipulate your data in your ViewModels, leaving your generated Kentico models intact.

  • Repositories allow you to quickly inject test data into your applications. This can simplify your CI/CD process.

  • You can use the Kentico Caching API within your MVC apps to improve performance and reduce database calls.

  • With Kentico 12 MVC, you have tremendous possibilities and functionality available. We will continue to add to the MVC architecture in future releases.
     

Up until now, my MVC site has been pretty basic, Sure, I added some fancy new widgets and editors, but I wasn’t really architecting the site using best practices. That’s all going to change in this final blog in the series! Get ready for some exciting times, covering Dependency Injection, view models, repositories, and even caching. Oh yes, this is truly a magical time to be alive!

A Very Brief Overview of Dependency Injection

Before we get into the fun code part, let’s take a moment to talk about Dependency Injection. This MVC-centric pattern allows for decoupling of code so dependent objects can be changed without updating the main code. This means your client code does not need to know about the injecting code or its dependencies. You can focus on what you need, leaving the heavy lifting to the DI. Your DI library will handle the registration and passing of any dependent objects/classes. This helps keep controllers simple, as all data retrieval is done in the service.

There are tons of articles on DI, so I added some links here in case you want to read up.

Choosing a Dependency Injection Library

So, we know we want DI, but how? Luckily, there are only about 1,000 different DI libraries to choose from. To make it easier for you, know that most of them are essentially doing the same thing in the end. As long as you are using something for DI, you’ll be in good shape. Sure, some have different levels of difficulty to implement and unique capabilities and quirks. Be sure to review a few and choose one that matches your skillset and architecture.

Our sample Dancing Goat uses Autofac, which is totally fine. Other popular libraries are LightInject, Ninject, Castle Windsor, and many others. For my site, I’m going with LightInject.

You can learn about different DI libraries here:

https://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx

https://www.lightinject.net/mvc/

https://autofac.org/

TIP

Adding in a third-party DI library is only needed for .NET Framework applications. With .NET Core, this is baked right into the platform! For EMS, we are continuing to work on our .NET Core support, but for now, you'll need to add a separate DI component to use in your Kentico 12 sites.

Adding View Models

Before I get to the DI fun, I want to add some view models to my site. Up until now, I’ve been using simple models to pass to my view. While this is fine to do, it’s not very flexible. By implementing view models, you can easily manipulate your data structures without affecting your generated Kentico models. This keeps your models and controllers clean and separated, which is what MVC is all about.

Here’s a view model I created for my Home page. In my case, my view model is pretty basic and just contains the root PageInfo object. I could easily add in more properties to handle a collection of articles and other properties. By using a view model, I can also change the HomePage class, as needed, without changing the generated code. 

public class HomePageViewModel { public HomePage PageInfo { get; set; } }

Adding Dependency Injection

OK, with my view models in place, I’m ready to add some DI action. Every library will be a little different, however, they almost all follow the same pattern for use:

  • Add NuGet packages
  • Register your controllers/services/repositories with the library
  • Pass the dependencies into your controllers, repositories, etc.
  • Profit!
     

With LightInject, here are the steps in action:

First, I add the LightInject NuGet packages. Note that I chose the LightInject.Mvc.Source because this is an MVC site.

LightInject 1

When you add these packages, Visual Studio will create some default files to include in your project. These files add all the LightInject goodness to your solution.

LightInject2

Next, you will want to create a file for your registrations. You could put these in the Global.asax file, but to keep things clean, I create a new file and just call the register method from my Applicaiton_Start method.

Here’s my configuration file.

namespace KenticoMVCWidgetShowcase.App_Start {     public static class LightInjectConfig     {         public static void Register()         {             // Register LightBox service container             var container = new ServiceContainer();             container.EnableMvc();             // Register the controllers             container.RegisterControllers(Assembly.GetExecutingAssembly());             // Register the services             container.Register<HomePageService>();             container.Register<MVCWidgetsPageService>();             container.Register<MVCWidgetService>();            container.Register<IMVCWidgetRepository, MVCWidgetRepository>();         }     } }

And here’s the Global.asax file.

    public class KenticoMVCWidgetShowcaseApplication : System.Web.HttpApplication     {         protected void Application_Start()         {             // Register LightInject configuration             LightInjectConfig.Register();             // Enables and configures selected Kentico ASP.NET MVC integration features             ApplicationConfig.RegisterFeatures(ApplicationBuilder.Current);             // Register the script bundles             BundleConfig.RegisterBundles(BundleTable.Bundles);             // Registers routes including system routes for enabled features            RouteConfig.RegisterRoutes(RouteTable.Routes);         }     }

Next, I create some services to use with LIghtInject, [SPOLER ALERT] My configuration file already has these defined.

I create a HomePageService class to retrieve my data.

    public class HomePageService     {         public HomePageViewModel GetHomePage()         {             HomePageViewModel vm = new HomePageViewModel();             string culture = "en-us";             string siteName = SiteContext.CurrentSiteName;             vm.PageInfo = HomePageProvider.GetHomePage("/home", culture, SiteContext.CurrentSiteName););                        return vm;         }     }

I then create an interface for the class.

    public class IHomePageService     {     }

My MVCWidgetService file follows the same pattern, however, it has more functions. This is because this service will return a collection of widgets as well as a single widget item.

   public class MVCWidgetService     {         public static MVCWidget GetMVCWidget(Guid guid)         {             string culture = "en-us";             string siteName = SiteContext.CurrentSiteName;             var widget  = MVCWidgetProvider.GetMVCWidget(guid, culture, siteName)                                                                 .TopN(1)                                                                 .FirstOrDefault();             return widget;         }         public static IEnumerable<MVCWidget> GetMVCWidgets(int SelectTopN)         {             string culture = "en-us";             string siteName = SiteContext.CurrentSiteName;             var widgets  = MVCWidgetProvider.GetMVCWidgets()                 .OnSite(SiteContext.CurrentSiteName)                 .Culture("en-us")                 .TopN(SelectTopN)                 .OrderByDescending("DocumentPublishFrom")                 .TypedResult; // Ensures that the result of the query is saved, not the query itself             return widgets;         }     }

After creating the services, I’m ready to pass them to my controllers. Here’s an example of HomeController. Note how the service is passed into the controller and assigned to the local variable. This is then used to retrieve the data.

    public class HomeController : Controller     {         private HomePageService _service;         public HomeController(HomePageService service)         {             this._service = service;         }         // GET: Home         public ActionResult Index()         {             HomePageViewModel vm = _service.GetHomePage();             // Returns a 404 error when the retrieving is unsuccessful             if (vm.PageInfo == null)             {                 return HttpNotFound();             }             // Initializes the page builder with the DocumentID of the page             HttpContext.Kentico().PageBuilder().Initialize(vm.PageInfo.DocumentID);             return View(vm);         }     }

Caching

With the services in place, I want to add some caching to improve performance. By leveraging Kentico’s built-in caching API, we can reduce our DB calls and improve how the site responds.

Here’s an updated HomePageService using the caching API to retrieve and store the data.

    public class HomePageService     {         public HomePageViewModel GetHomePage()         {             HomePageViewModel vm = new HomePageViewModel();             string culture = "en-us";             string siteName = SiteContext.CurrentSiteName;             Func<HomePage> dataLoadMethod = () => HomePageProvider.GetHomePage("/home", culture, SiteContext.CurrentSiteName);             var cacheSettings = new CacheSettings(10, siteName + "|data|homepage", siteName, culture)             {                 GetCacheDependency = () =>                 {                     // Creates caches dependencies. This example makes the cache clear data when any article is modified, deleted, or created in Kentico.                     string dependencyCacheKey = String.Format("nodes|{0}|{1}|all", siteName, HomePage.CLASS_NAME.ToLowerInvariant());                     return CacheHelper.GetCacheDependency(dependencyCacheKey);                 }             };             vm.PageInfo = CacheHelper.Cache(dataLoadMethod, cacheSettings);                        return vm;         }     }

NOTE

Caching the data using the Kentico API only stores the data in the MVC site’s app pool. This means it’s not visible within the Kentico Admin System module.

Repositories

The last update to the site is to add repositories. Repositories allow you to separate your data layer even further but also offer a way to load whatever data you want into your models. While this is typically from the Kentico DB, you can also use test data within your application to simulate the content.

You can learn more about using repositories in your Kentico sites here:

Testing in MVC Controllers

Here’s my MVCWidgetRepository file. Note that I am calling my MVCWidgetService functions inside the repository to leverage that code.

    public sealed class MVCWidgetRepository : IMVCWidgetRepository     {         public MVCWidget GetMVCWidget(Guid guid)         {             return MVCWidgetService.GetMVCWidget(guid);         }         public IEnumerable<MVCWidget> GetMVCWidgets(int intSelectTopN = 0)         {             // Get the collection of MVC Widgets             return MVCWidgetService.GetMVCWidgets(intSelectTopN);         }     }

Also, don’t forget to register your repos in your LightInject file!

            // Register the repositories             container.Register<IMVCWidgetRepository, MVCWidgetRepository>();

Testing

Wow! That was a lot of code! And the funny thing, there isn't any visual change to the site. We’ve just made our MVC better architected and improved its performance. But just in case, here’s a pic of the Home page using the new code. It looks identical to the previous version but is now leveraging DI, caching, and repositories to load and display the data much more efficiently.

Testing 1

The Future

OK, I warned you this blog would be long. The thing is, with MVC, there’s so many possibilities. It’s very easy to add all sorts of awesome capabilities to your site. It just depends on how much you want to do and what works best for your functionality. Whether it’s a vanilla MVC site, or one with translations, search, and EMS features, building Kentico sites with MVC is the future.

I hope this blog series brought you a little closer to starting your next MVC project. With Kentico 12, you can build amazingly fast and dynamic applications, all while leveraging a ton of great functionality. Good luck friends!

By Bryan Soltis in MVC
Need to make your transition to MVC go smoothly? Well, we have a page for that!
Show Me
search
We're named a Strong Performer in the Q4 2018
Forrester Wave™ for WCM!
×