"
 
 
 
ASP.NET (snapshot 2017) Microsoft documentation and samples

Application Parts in ASP.NET Core

View or download sample code ((xref:)how to download)

An Application Part is an abstraction over the resources of an application, from which MVC features like controllers, view components, or tag helpers may be discovered. One example of an application part is an AssemblyPart, which encapsulates an assembly reference and exposes types and compilation references. Feature providers work with application parts to populate the features of an ASP.NET Core MVC app. The main use case for application parts is to allow you to configure your app to discover (or avoid loading) MVC features from an assembly.

Introducing Application Parts

MVC apps load their features from application parts. In particular, the AssemblyPart class represents an application part that is backed by an assembly. You can use these classes to discover and load MVC features, such as controllers, view components, tag helpers, and razor compilation sources. The ApplicationPartManager is responsible for tracking the application parts and feature providers available to the MVC app. You can interact with the ApplicationPartManager in Startup when you configure MVC:

By default MVC will search the dependency tree and find controllers (even in other assemblies). To load an arbitrary assembly (for instance, from a plugin that isn’t referenced at compile time), you can use an application part.

You can use application parts to avoid looking for controllers in a particular assembly or location. You can control which parts (or assemblies) are available to the app by modifying the ApplicationParts collection of the ApplicationPartManager. The order of the entries in the ApplicationParts collection is not important. It is important to fully configure the ApplicationPartManager before using it to configure services in the container. For example, you should fully configure the ApplicationPartManager before invoking AddControllersAsServices. Failing to do so, will mean that controllers in application parts added after that method call will not be affected (will not get registered as services) which might result in incorrect bevavior of your application.

If you have an assembly that contains controllers you do not want to be used, remove it from the ApplicationPartManager:

In addition to your project’s assembly and its dependent assemblies, the ApplicationPartManager will include parts for Microsoft.AspNetCore.Mvc.TagHelpers and Microsoft.AspNetCore.Mvc.Razor by default.

Application Feature Providers

Application Feature Providers examine application parts and provide features for those parts. There are built-in feature providers for the following MVC features:

Feature providers inherit from IApplicationFeatureProvider<T>, where T is the type of the feature. You can implement your own feature providers for any of MVC’s feature types listed above. The order of feature providers in the ApplicationPartManager.FeatureProviders collection can be important, since later providers can react to actions taken by previous providers.

Sample: Generic controller feature

By default, ASP.NET Core MVC ignores generic controllers (for example, SomeController<T>). This sample uses a controller feature provider that runs after the default provider and adds generic controller instances for a specified list of types (defined in EntityTypes.Types):

[!code-csharpMain]

   1:  using Microsoft.AspNetCore.Mvc.ApplicationParts;
   2:  using Microsoft.AspNetCore.Mvc.Controllers;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using System.Reflection;
   6:  using AppPartsSample.Model;
   7:   
   8:  namespace AppPartsSample
   9:  {
  10:      // An IApplicationFeatureProvider<ControllerFeature> will be used at startup time to discover types
  11:      // that should be treated as controllers. Normally MVC will ignore an open generic type like
  12:      // GenericController<>.
  13:      //
  14:      // This component will create a new type (like GenericController<Widget>) for each entity type
  15:      // that does not already have a non-generic controller defined. We determine this based on the type
  16:      // name. So because SprocketController is defined, we don't add GenericController<Sprocket> as it
  17:      // would create a conflict.
  18:      public class GenericControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
  19:      {
  20:          public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
  21:          {
  22:              // This is designed to run after the default ControllerTypeProvider, 
  23:              // so the list of 'real' controllers has already been populated.
  24:              foreach (var entityType in EntityTypes.Types)
  25:              {
  26:                  var typeName = entityType.Name + "Controller";
  27:                  if (!feature.Controllers.Any(t => t.Name == typeName))
  28:                  {
  29:                      // There's no 'real' controller for this entity, so add the generic version.
  30:                      var controllerType = typeof(GenericController<>)
  31:                          .MakeGenericType(entityType.AsType()).GetTypeInfo();
  32:                      feature.Controllers.Add(controllerType);
  33:                  }
  34:              }
  35:          }
  36:      }
  37:  }

The entity types:

[!code-csharpMain]

   1:  using System.Collections.Generic;
   2:  using System.Reflection;
   3:   
   4:  namespace AppPartsSample.Model
   5:  {
   6:      public static class EntityTypes
   7:      {
   8:          public static IReadOnlyList<TypeInfo> Types => new List<TypeInfo>()
   9:          {
  10:              typeof(Sprocket).GetTypeInfo(),
  11:              typeof(Widget).GetTypeInfo(),
  12:          };
  13:   
  14:          public class Sprocket { }
  15:          public class Widget { }
  16:      }
  17:  }

The feature provider is added in Startup:

By default, the generic controller names used for routing would be of the form GenericController`1[Widget] instead of Widget. The following attribute is used to modify the name to correspond to the generic type used by the controller:

[!code-csharpMain]

   1:  using Microsoft.AspNetCore.Mvc.ApplicationModels;
   2:  using System;
   3:   
   4:  namespace AppPartsSample
   5:  {
   6:      // Used to set the controller name for routing purposes. Without this convention the
   7:      // names would be like 'GenericController`1[Widget]' instead of 'Widget'.
   8:      //
   9:      // Conventions can be applied as attributes or added to MvcOptions.Conventions.
  10:      [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
  11:      public class GenericControllerNameConvention : Attribute, IControllerModelConvention
  12:      {
  13:          public void Apply(ControllerModel controller)
  14:          {
  15:              if (controller.ControllerType.GetGenericTypeDefinition() != 
  16:                  typeof(GenericController<>))
  17:              {
  18:                  // Not a GenericController, ignore.
  19:                  return;
  20:              }
  21:   
  22:              var entityType = controller.ControllerType.GenericTypeArguments[0];
  23:              controller.ControllerName = entityType.Name;
  24:          }
  25:      }
  26:  }

The GenericController class:

[!code-csharpMain]

   1:  using Microsoft.AspNetCore.Mvc;
   2:   
   3:  namespace AppPartsSample
   4:  {
   5:      [GenericControllerNameConvention] // Sets the controller name based on typeof(T).Name
   6:      public class GenericController<T> : Controller
   7:      {
   8:          public IActionResult Index()
   9:          {
  10:              return Content($"Hello from a generic {typeof(T).Name} controller.");
  11:          }
  12:      }
  13:  }

The result, when a matching route is requested:

Example output from the sample app reads, ‘Hello from a generic Sproket controller.’
Example output from the sample app reads, ‘Hello from a generic Sproket controller.’

Sample: Display available features

You can iterate through the populated features available to your app by requesting an ApplicationPartManager through dependency injection and using it to populate instances of the appropriate features:

[!code-csharpMain]

   1:  using AppPartsSample.ViewModels;
   2:  using Microsoft.AspNetCore.Mvc;
   3:  using Microsoft.AspNetCore.Mvc.ApplicationParts;
   4:  using Microsoft.AspNetCore.Mvc.Controllers;
   5:  using System.Linq;
   6:  using Microsoft.AspNetCore.Mvc.Razor.Compilation;
   7:  using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
   8:  using Microsoft.AspNetCore.Mvc.ViewComponents;
   9:   
  10:  namespace AppPartsSample.Controllers
  11:  {
  12:      public class FeaturesController : Controller
  13:      {
  14:          private readonly ApplicationPartManager _partManager;
  15:   
  16:          public FeaturesController(ApplicationPartManager partManager)
  17:          {
  18:              _partManager = partManager;
  19:          }
  20:   
  21:          public IActionResult Index()
  22:          {
  23:              var viewModel = new FeaturesViewModel();
  24:   
  25:              var controllerFeature = new ControllerFeature();
  26:              _partManager.PopulateFeature(controllerFeature);
  27:              viewModel.Controllers = controllerFeature.Controllers.ToList();
  28:   
  29:              var metaDataReferenceFeature = new MetadataReferenceFeature();
  30:              _partManager.PopulateFeature(metaDataReferenceFeature);
  31:              viewModel.MetadataReferences = metaDataReferenceFeature.MetadataReferences
  32:                                              .ToList();
  33:   
  34:              var tagHelperFeature = new TagHelperFeature();
  35:              _partManager.PopulateFeature(tagHelperFeature);
  36:              viewModel.TagHelpers = tagHelperFeature.TagHelpers.ToList();
  37:   
  38:              var viewComponentFeature = new ViewComponentFeature();
  39:              _partManager.PopulateFeature(viewComponentFeature);
  40:              viewModel.ViewComponents = viewComponentFeature.ViewComponents.ToList();
  41:   
  42:              return View(viewModel);
  43:          }
  44:      }
  45:  }

Example output:

Example output from the sample app
Example output from the sample app




Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/AspNet-DocAndSamples-2017/aspnetcore/mvc/advanced/app-parts.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>