Re-use Controllers, Views and Tag-helpers in ASP.NET Core

I recently ran into a situation, where I needed to implement the same functionality, the same views and even the same tag-helpers for different customers.

Like most other developers I hate copy-pasting code that should be shared across multiple projects. I had come across a similar need a few years back, but remembered that it was cumbersome in ASP.NET 3-5, to achieve this, and the solutions back then didn’t feel right. But since my recent switch to dotnet core, I knew there was a chance that Microsoft had a prepared an early christmas present for me.

So I started researching my options, searching for titles similar to this blog post, and finally found a solution on the third place on google (Yes I was that desperate).

Introducing Application Parts / App Parts

With the introduction of ASP.NET Core v1.0, MVC came with a new concept called “Application Parts”, which is how the features and resources is populated into a ASP.NET Core MVC application.

MVC loads it’s features from different application parts, which is a new type in ASP.NET Core. Specifically for my use-case, it can load the parts of an application from another Assembly (read: dll file). By creating an AssemblyPart object, you can discover controllers, views and tag helpers from a shared library.

Adding controllers from a shared project

Consider that I have a basic MVC site called “MyWebApp”, and another project called “SharedModule”, of which MyWebApp has a reference to. The SharedModule is created as an Empty MVC site, and then later populated with controllers.

The MyWebApp project only contains a HomeController, and the “SharedModule” contains an MVC Controller called “UserController”, that can be shared by multiple projects.

Structure:

  • Solution
    • MyWebApp
      • HomeController
    • SharedModule
      • UserController

By default, the MyWebApp application, will not be able to serve the UserController in the other project, unless it inherits it directly within its own site. But by using application parts, this becomes possible.

// Get assembly type that UserController lives in. 
var assembly = typeof(UserController).GetTypeInfo().Assembly;
services.AddMvc().AddApplicationPart(assembly);

The above code is a shortcut for doing this directly with the ApplicationPartManager.

// Get assembly type that UserController lives in. 
var assembly = typeof(UserController).GetTypeInfo().Assembly;
// Create new assembly part, from assembly type
var sharedModulePart = new AssemblyPart(assembly);
services.AddMvc()
    // Configure the Application Manager
    .ConfigureApplicationPartManager(apm => 
        // Add the SharedModule assembly part, to the application parts list.
        apm.ApplicationParts.Add(sharedModulePart));

The above code examples, loads in all the application parts from the SharedModulewhich in this case, means the UserController.

Managing Application Features

While Application Parts lets you load in controllers, views etc. from other assemblies, the Application Feature provider list within the ApplicationPartManager lets you fine tune exactly what goes into your application. 

By defining and adding an IApplicationFeatureProvider implementation to the FeatureProviders list, you can add, remove or even edit the features that makes up your application.

 services.AddMvc()
     .AddApplicationPart(assembly);            
     .ConfigureApplicationPartManager(apm => 
         // Add the FeatureProvider that can filter out controllers.
         apm.FeatureProviders.Add(new FilterControllerFeatureProvider()));
public class FilterControllerFeatureProvider: IApplicationFeatureProvider<ControllerFeature>
{
    public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
    {
        // Find controller in the ControllerFeature object.
        var controllerToRemove = feature.Controllers.FirstOrDefault(x => x.Name == "UserController");
        // Remove the controller we added from Application Parts.
        feature.Controllers.Remove(controllerToRemove);
    }
}

It seems a bit odd to remove the controller we just spent time on adding, but the functionality is great, and I use it to fine tune what exact components goes into my applications. By using configuration, I can go through each of my controllers and decide whether a certain feature is enabled for that customer.

And it becomes extremely powerful, if you start to consider generic Controllers, that can handle basic CRUD operations for a bunch of different models.

Microsoft has provided a great example of that on their documentation page.

Which I encourage you to go read to learn more about Application Parts in ASP.NET Core.

I hope this blog post was helpful, I certainly learned a lot researching this topic, and will be using Application Parts a fair bit, for those pieces of code that absolutely should be shared and not just copied around.

// André Kock

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *