Using MVC ASP.NET Core APPs in a Host ASP.NET Core Application

This article shows how ASP.NET Core applications could be deployed inside a separate host ASP.NET Core MVC application. This could be useful if you have separate applications, services, or layouts but want to have a common user interface, or common deployment to improve the user experience.

Code: https://github.com/damienbod/MultipleMvcApps

Setup

The applications are split into four different projects to demonstrate some of the possibilities. Two applications are complete ASP.NET Core MVC applications with a database, separate services, use localization and a unique layout.

The host application references the child MVC applications and the shared project contains the localizations for all applications.

The projects can be added as references in visual studio.

ASP.NET Core Areas

The MVC applications are referenced into the host application and use areas per application. This needs to be added to the route configuration. The areas route is added first, and then the default routes for the host application views and controllers.

Host application routing:

app.UseMvc(routes =>
{
	routes.MapRoute(
	  name: "areas",
	  template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
	);

	routes.MapRoute(
		name: "default",
		template: "{controller=Home}/{action=Index}/{id?}");
});

In the child applications, the UI logic is added to an area. The area attribute MUST be added to the MVC controller. If your area controller, view is not working, this is usually the problem.

Example of a controller in one of the child applications:

namespace MvcApp1.Controllers
{
    [Area("MvcApp1")]
    public class HomeController : Controller
    {

Area setup solution explorer:

ASP.NET Core Layouts

Each application uses its own layout. This is defined in the _ViewStart.cshtml file. This makes it really easy to have application specific layouts, even when hosting inside the separate application.

One problem with this, is that all applications use the same css and js files, ie the ones built and deployed in the host application. This means that the html header and the javascript links all need to match the host project. Application specific css or javascript files would need to be deployed and included then in the host application if this is required. Inline css and javascript would be deployed as part of the child views, but this should be avoided.

The demo application uses bootstrap 4 with npm and bundleconfig. The different layouts have separate background colors to demonstrate.

ASP.NET Core Navigation

The navigation between the different areas needs to be changed, because different areas or applications are used to implement the MVC apps.

The href is called using the path, for example “~/Home/Index”. This will then work for all the different applications, areas.

<nav class="bg-dark mb-4 navbar navbar-dark navbar-expand-md">
  <a href="~/Home/Index" class="navbar-brand">
	<em>HO</em>
  </a>
  <button aria-controls="navbarCollapse" 
	aria-expanded="false" 
	aria-label="Toggle navigation" 
	class="navbar-toggler" 
	data-target="#topNavbarCollapse" 
	data-toggle="collapse" type="button">
	<span class="navbar-toggler-icon"></span>
  </button>
  <div class="collapse navbar-collapse" id="topNavbarCollapse">
	<ul class="mr-auto navbar-nav">
	  <li class="nav-item">
		<a href="~/Home/Index" class="nav-link">
		@SharedLocalizer.GetLocalizedHtmlString("HOME HOST")
		</a>
	  </li>
	  <li class="nav-item">
		<a href="~/MvcApp1/Home/Index" 
		class="nav-link">MvcApp1</a>
	  </li>
	  <li class="nav-item">
		<a href="~/MvcApp2/Home/Index" 
		class="nav-link">MvcApp2</a>
	  </li>
	</ul>
  </div>
</nav>

IoC and Services

Each application has its own services, which are only required by the application itself. The different applications have also services required by all three separate projects.

The common services can be added directly to the host application. These are also added to the child applications, but only required to test or build.

The specific services can be added in a separate extension class and used in both the host application and the child application. The ServicesExtensions class implements the services for the child application. A database context could be added here, or whatever.

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MvcApp1.Models;

namespace MvcApp1
{
 public static class ServicesExtensions
  {
    public static void AddMvcApp1(
     this IServiceCollection services, IConfiguration configuration)
    {
      services.AddSingleton<ExampleService>();

      services.AddDbContext<SomeDataContext>(options =>
       options.UseSqlServer(
       configuration.GetConnectionString("SomeDataContext")
	   )
	  );
  }
 }
}

This is then used in the host application as well as the child application. The configurations for the child applications must be added to the host application configuration.

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
	services.AddMvcApp1(Configuration);

	services.AddMvcApp2(Configuration);

Localization

The localization for all the projects is implemented in a shared project. This is then used in the host application as a shared localization.


  services.AddSingleton<LocService>();
  services.AddLocalization(options => 
	options.ResourcesPath = "Resources");

  services.Configure<RequestLocalizationOptions>(
	options =>
	{
		var supportedCultures = new List<CultureInfo>
			{
				new CultureInfo("en-US"),
				new CultureInfo("de-CH")
			};

		options.DefaultRequestCulture = new RequestCulture(
			culture: "en-US", 
			uiCulture: "en-US");
		options.SupportedCultures = supportedCultures;
		options.SupportedUICultures = supportedCultures;

		options.RequestCultureProviders.Insert(0, 
			new QueryStringRequestCultureProvider());
	});

  services.AddMvc()
	.AddViewLocalization()
	.AddDataAnnotationsLocalization(options =>
	{
	options.DataAnnotationLocalizerProvider = (type, factory) =>
	{
		var assemblyName = new AssemblyName(
			typeof(SharedResource).GetTypeInfo().Assembly.FullName);
		return factory.Create("SharedResource", assemblyName.Name);
	};
  }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
  ...

  var locOptions = 
   app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
  app.UseRequestLocalization(locOptions.Value);

  ...
}

To build the child applications, the LocService must also be added if this is used in one of the views, or a service.

Notes

This works with very little effort and uses ASP.NET Core more or less as it is, but also has room for lots of improvements. For example, the application specific services are registered in the host and are registered for the whole host, not just the child application. This could be improved by having application specific services. The shared css and javascript could also be optimized, maybe through an internal npm or something like that.

By hosting the different applications inside a single hosted application, the user experience can be greatly improved, the application security can be simplified, the deployment complexity is reduced and the logic remains separated.

Links

https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/areas?view=aspnetcore-2.1

Advertisements

2 comments

  1. […] Using MVC ASP.NET Core APPs in a Host ASP.NET Core Application (Damien Bowden) […]

  2. Author describe this blog elaborately. It is a nice and descriptive comment as my point of view. Please publish this type of blog and update follower like me.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: