Dynamically load ControllerBase implementations from assemblies - c#

I'm trying to create a web service using ASP.NET Core 2.1 where I need the service to be able to register BaseControllers loaded from DLL's through reflection. However I can't seem to find how to register a BaseController to the service configuration.
In Startup.cs
public class Startup
{
....
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
/// This now loads the base controllers located
/// within the dll's through reflection and the
/// BaseControllers are initialized as well
IList<ControllerBase> controllers = PluginLoader.Instance.GetControllers();
foreach (ControllerBase controllerBase in controllers)
{
/// Here I hope that I can add the controllerBase to services but
/// I just can't seem to find a way to do it. Is it even possible?
/// I'm thinking that builder.AddControllersAsServices() might be useful for something but just can't seem to get it right
}
}
}
Does anyone have any tips?

It seems like it's possible to add an assembly to the builder and that will make the controller(s) available. So it's achievable with a minor refactor of the loader.
Tested code:
if (controllers != null && controllers.Any())
{
foreach (ControllerBase controllerBase in controllers)
{
builder.AddApplicationPart(controllerBase.GetType().Assembly);
}
}

Related

How do I use dll of .net 5 class library that contain in its class Dependency injection?

In my Asp.net Core 5 API Project
I have a serviceLayer that the controller uses, to get data from a third layer called dataLayer.
I want to use the service layer as a DLL in different projects.
This ServiceLayer Contain dependency Injections like that :
namespace ServiceLayer
{
public class UserService : IUserService
{
IUserRepository userRepository; // (From DataLayer)
public UserService(IUserRepository repository) : base(repository)
{
this.userRepository = repository;
}
public Users GetAllPersonsById(int id)
{
return userRepository.GetById(id);
}
}
public interface IUserService : IService<Users>
{
Users GetAllPersonsById(int id);
}
How can I use the method GetAllPersonsById with the DLL ServiceLayer
can I use it because the dependency Injections
As soon as you reference the DLL / project you can use all classes the same ways as if they were in the project.
To use a class as a service:
Provide the service
Inject the service
There's a lot of documentation available, so I'll keep this short:
// provide in startup.cs
services.AddTransient<IUserService, UserService>();
// Inject where you need it
MyConstructor(IUserService userService) {}
See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0
Provide Extension Method
If we take a look at other libs, most of them provide a method to setup the services.
Example: Entity framework core
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(...));
}
So you could:
In your lib, create an extension method for IServicesCollection that adds all services of your lib.
In the consuming project, call services.AddMyLibServices().
This could look like so:
public static class ServicesConfiguration
{
public static void AddDataLayer(this IServiceCollection services)
{
services.AddTransient<IUserService, UserService>();
// ... same for all services of your lib
}
}
Here's a tutorial with more details:
https://dotnetcoretutorials.com/2017/01/24/servicecollection-extension-pattern/
Lamar service registries
An optional and alternative approach are service registries. It's very similar to the extension methods but uses a class to do the setup. See https://jasperfx.github.io/lamar/documentation/ioc/registration/registry-dsl/
Composition Root
You may want to read about the composition root pattern, e.g. What is a composition root in the context of dependency injection?
In a simple app, your startup.cs is your composition root. In more complex apps, you could create a separate project to have a single place to configure your apps services.
Create the DLL
There are two ways to create the DLL:
As a project in your solution (so your solution has multiple projects, each will result in a separate DLL)
As a separate solution and as nuget package

Extending API in one project fully exposes referenced API but routes clash

I'm trying to split a Core 2.1 WebAPI project into two in order that we can expose two different APIs according to circumstances. Simplified, we have one API and we want all the read-only (GET) requests in one API and the entire set in another (the "admin" API). Swagger is enabled in the projects.
I duplicated the project, renaming one (namespaces, etc.) and adding both to the same solution, then commented out all the non-GET controller methods in the read-only project and commented out all the GET methods in the admin project. I then added a reference to the read-only project in the admin project.
Running the read-only project, the swagger page came up fine, just the GETs. Running the admin project gave a 500 on the swagger page. Interestingly, during debugging, I found that removing All the controllers from the admin project, the underlying API from the read-only project was completely exposed straight through and appeared fully functional - not something I was expecting and a potential security issue for anyone not expecting it.
However, I then added one controller back and changed it to decend from one of the read-only controllers, over-riding the ancestor contructor, etc. - it still gave a 500.
Base class:
namespace InfoFeed.WebAPI.Features.Account
{
/// <summary>
/// Handle user account related tasks
/// </summary>
[Authorize]
[Produces("application/json")]
[Route("api/account")]
public class AccountController : Controller
{
private readonly ILogger _log;
protected readonly IMediator _mediator;
public AccountController(ILogger<AccountController> log,
IMediator mediator)
{
_log = log;
_mediator = mediator;
}
Descendent class:
namespace InfoFeedAdmin.WebAPI.Features.Account
{
/// <summary>
/// Handle user account related tasks
/// </summary>
[Authorize]
[Produces("application/json")]
[Route("api/account")]
public class AccountAdminController
: InfoFeed.WebAPI.Features.Account.AccountController
{
public AccountAdminController(ILogger<AccountAdminController> log,
IMediator mediator)
: base(log, mediator)
{
}
I thought that perhaps the route might be causing a clash so I tried changing that to [Route("api/admin/account")] - this worked as long as there were no clashing method signatures. However, it means that there are two sets of routes exposed to the same underlying controller methods.
POST /api/account/signin
GET /api/account/signout
POST /api/admin/account/signin
GET /api/admin/account/signout
Does anyone know how I can hide (perhaps selectively) the routes from the ancestor class so that only the routes I choose to expose from the descendent class are visible/accessible?
Cheers
By default MVC will search the dependency tree and find controllers (even in other assemblies).
You can use application parts to avoid looking for controllers in a particular assembly or location.
If you have an assembly that contains controllers you don't want to be used, remove it from the ApplicationPartManager:
services.AddMvc()
.ConfigureApplicationPartManager(apm =>
{
var dependentLibrary = apm.ApplicationParts
.FirstOrDefault(part => part.Name == "DependentLibrary");
if (dependentLibrary != null)
{
p.ApplicationParts.Remove(dependentLibrary);
}
})
Source: https://learn.microsoft.com/en-us/aspnet/core/mvc/advanced/app-parts?view=aspnetcore-2.1

How can I split up my service registations into different files in ASP.NET Core?

If you've ever used Windsor Installers then you know what I'm talking about. You can have different installers for different parts of the application (repositories, logic, etc.)
public class RepositoriesInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(...);
}
}
And then we could call the installer from the Windsor bootstrapper like so
var container = new WindsorContainer();
container.Install(new RepositoriesInstaller());
Does asp.net core have anything similar?
If not I would implement it like so
public interface IServiceConfiguration
{
void Install(IServiceCollection services);
}
public class DomainServiceConfiguration : IServiceConfiguration
{
public void Install(IServiceCollection services)
{
services.AddScoped(...);
}
}
And then call this from the startup file
new DomainServiceConfiguration().Install(services);
So as many people mentioned in the comments, the IOC container in asp.net core is simple and light weight. However it is extensible.
The convention would be to write an extension method in a different file and call in the ConfigureServices method (So core's DI has no such thing as a module installer interface). This answers the question I posted originally.
My argument was that you would not be able to register the methods by convention. I.e. create the extension method and not have to worry about adding it to the ConfigureServices class.
I was wrong, this can be done. Here's the code:
This class lives in any .cs file within your assembly
public static class MyInstaller
{
public static void Install(this IServiceCollection services)
{
// register stuff here
}
}
Add This Method to your Startup.cs file
void LoadInstaller(Type type, IServiceCollection services)
{
var installMethods= type.GetMethods(BindingFlags.Static | BindingFlags.Public).Where(mi => mi.Name == "Install");
var installMethod = installMethods.First();
installMethod.Invoke(null, new object[] { services });
}
Add this to your ConfigureServices method
var assemblies = AppDomain.CurrentDomain.GetAssemblies().
Where(assembly => assembly.GetName().Name.Contains("MyAssemblyName"));
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes().Where(t => t.IsClass && t.IsPublic && t.Name.Contains("Installer")); // You can create your own convention here, make sure it won't conflict with other class names that are not meant to be installers
foreach (var installerType in types)
{
LoadInstaller(installerType, services);
}
}
Now you can add installers by convention and you never have to manually call the extension method for each new installer. The code could be cleaned up by adding error handling and throwing useful exceptions. But the idea is sound.
EDIT:
Seeing that this answer seems to gaining attention, I thought I'd add Microsoft's recommendation to a similar problem
Each services.Add{SERVICE_NAME} extension method adds, and potentially
configures, services. We recommended that apps follow this convention.
Place extension methods in the
Microsoft.Extensions.DependencyInjection namespace to encapsulate
groups of service registrations. Including the namespace portion
Microsoft.Extensions.DependencyInjection for DI extension methods
also:
Allows them to be displayed in IntelliSense without adding additional
using blocks. Prevents excessive using statements in the Program or
Startup classes where these extension methods are typically called.

Unity with ASP.NET Core and MVC6 (Core)

Update 09.08.2018
Unity is being developed here but I haven't had the time to test how it plays with the ASP.NET Core framework.
Update 15.03.2018
This solution is for the specific problem of using ASP.NET Core v1 with Unity while using the .NET Framework 4.5.2 NOT the .NET Core Framework. I had to use this setup since I needed some .Net 4.5.2 DLLs but for anyone starting afresh I would not recommend this approach. Also Unity is not being developed any further (to my knowlage) so I would recommend using the Autofac Framework for new projects. See this Post for more info on how to do that.
Intro
I am building a Web Application using ASP.NET with MVC. This Application depends on certain services (a WCF Service a Datastore service etc). Now to keep things nice and decoupled I want to use a DI (Dependecy Injection) Framework, specifically Unity.
Initial Research
I found this blog post but sadly its not working. The idea though is nice. It basically says that you should not register all the services registered in the ServiceCollection into your own container, but rather reference the default ServiceProvider. So. if something needs to be resolved the default ServiceProvider is called and in case it has no resolution the type will be resolved using your custom UnityContainer.
The Problems
MVC always tries to resolve the Controller with the default ServiceProvider. Also, I noticed that even if the Controller would get resolved correctly, I can never "mix" Dependencies. Now, if I want to use one of my Services but also an IOptions interface from ASP the class can never be resolved because not one of those two containers has resolutions for both types.
What I need
So to recap I need the following things:
A setup where I dont need to copy ASP.NET Dependencies into my UnityContainer
A container which can resolve my MVC Controllers
A container which can resolve "mixed" Dependencies
EDIT:
So the question is how can I achieve these points ?
Environment
project.json:
So after some research I came up with the following solutions to my problems:
Use Unity with ASP
To be able to use Unity with ASP I needed a custom IServiceProvider (ASP Documentation) so I wrote a wrapper for the IUnityContainer which looks like this
public class UnityServiceProvider : IServiceProvider
{
private IUnityContainer _container;
public IUnityContainer UnityContainer => _container;
public UnityServiceProvider()
{
_container = new UnityContainer();
}
#region Implementation of IServiceProvider
/// <summary>Gets the service object of the specified type.</summary>
/// <returns>A service object of type <paramref name="serviceType" />.-or- null if there is no service object of type <paramref name="serviceType" />.</returns>
/// <param name="serviceType">An object that specifies the type of service object to get. </param>
public object GetService(Type serviceType)
{
//Delegates the GetService to the Containers Resolve method
return _container.Resolve(serviceType);
}
#endregion
}
Also I had to change the Signature of the ConfigureServices method in my Startup class from this:
public void ConfigureServices(IServiceCollection services)
to this:
public IServiceProvider ConfigureServices(IServiceCollection services)
Now I can return my custom IServiceProvider and it will be used instead of the default one.The full ConfigureServices Method is shown in the Wire up section at the bottom.
Resolving Controllers
I found this blog post. From it I learned that MVC uses an IControllerActivator interface to handle Controller instantiation. So I wrote my own which looks like this:
public class UnityControllerActivator : IControllerActivator
{
private IUnityContainer _unityContainer;
public UnityControllerActivator(IUnityContainer container)
{
_unityContainer = container;
}
#region Implementation of IControllerActivator
public object Create(ControllerContext context)
{
return _unityContainer.Resolve(context.ActionDescriptor.ControllerTypeInfo.AsType());
}
public void Release(ControllerContext context, object controller)
{
//ignored
}
#endregion
}
Now if a Controller class is activated it will be instatiated with my UnityContainer. Therefore my UnityContainer must know how to Resolve any Controller!
Next Problem: Use the default IServiceProvider
Now if I register services such as Mvc in ASP.NET I normally would do it like this:
services.AddMvc();
Now if I use a UnityContainer all the MVC Dependencies could not be Resolved because they aren't Registered. So I can either Register them (like AutoFac) or I can create a UnityContainerExtension. I opted for the Extension and came up with following two clases :
UnityFallbackProviderExtension
public class UnityFallbackProviderExtension : UnityContainerExtension
{
#region Const
///Used for Resolving the Default Container inside the UnityFallbackProviderStrategy class
public const string FALLBACK_PROVIDER_NAME = "UnityFallbackProvider";
#endregion
#region Vars
// The default Service Provider so I can Register it to the IUnityContainer
private IServiceProvider _defaultServiceProvider;
#endregion
#region Constructors
/// <summary>
/// Creates a new instance of the UnityFallbackProviderExtension class
/// </summary>
/// <param name="defaultServiceProvider">The default Provider used to fall back to</param>
public UnityFallbackProviderExtension(IServiceProvider defaultServiceProvider)
{
_defaultServiceProvider = defaultServiceProvider;
}
#endregion
#region Overrides of UnityContainerExtension
/// <summary>
/// Initializes the container with this extension's functionality.
/// </summary>
/// <remarks>
/// When overridden in a derived class, this method will modify the given
/// <see cref="T:Microsoft.Practices.Unity.ExtensionContext" /> by adding strategies, policies, etc. to
/// install it's functions into the container.</remarks>
protected override void Initialize()
{
// Register the default IServiceProvider with a name.
// Now the UnityFallbackProviderStrategy can Resolve the default Provider if needed
Context.Container.RegisterInstance(FALLBACK_PROVIDER_NAME, _defaultServiceProvider);
// Create the UnityFallbackProviderStrategy with our UnityContainer
var strategy = new UnityFallbackProviderStrategy(Context.Container);
// Adding the UnityFallbackProviderStrategy to be executed with the PreCreation LifeCycleHook
// PreCreation because if it isnt registerd with the IUnityContainer there will be an Exception
// Now if the IUnityContainer "magically" gets a Instance of a Type it will accept it and move on
Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
}
#endregion
}
UnityFallbackProviderStrategy:
public class UnityFallbackProviderStrategy : BuilderStrategy
{
private IUnityContainer _container;
public UnityFallbackProviderStrategy(IUnityContainer container)
{
_container = container;
}
#region Overrides of BuilderStrategy
/// <summary>
/// Called during the chain of responsibility for a build operation. The
/// PreBuildUp method is called when the chain is being executed in the
/// forward direction.
/// </summary>
/// <param name="context">Context of the build operation.</param>
public override void PreBuildUp(IBuilderContext context)
{
NamedTypeBuildKey key = context.OriginalBuildKey;
// Checking if the Type we are resolving is registered with the Container
if (!_container.IsRegistered(key.Type))
{
// If not we first get our default IServiceProvider and then try to resolve the type with it
// Then we save the Type in the Existing Property of IBuilderContext to tell Unity
// that it doesnt need to resolve the Type
context.Existing = _container.Resolve<IServiceProvider>(UnityFallbackProviderExtension.FALLBACK_PROVIDER_NAME).GetService(key.Type);
}
// Otherwise we do the default stuff
base.PreBuildUp(context);
}
#endregion
}
Now if my UnityContainer has no Registration for something it just ask the default Provider for it.
I learned all of this from several different articles
MSDN Unity article
Auto-Mocking Unity Container Extension
Custom Object Factory Unity Extension
The nice thing about this approach is that I can also "mix" Dependencies now. If I need any of my Services AND an IOptions Interface from ASP my UnityContainer will resolve all of these Dependencies and Inject them into my Controller !!! The only thing to remember is that if I use any of my own Dependencies I have to register my Controller class with Unity because the default IServiceProvider can no longer Resolve my Controllers Dependencies.
Finally: Wire up
Now in my project I use different services (ASP Options, MVC with options). To make it all work my ConfigureServices Method looks like this now:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add all the ASP services here
// #region ASP
services.AddOptions();
services.Configure<WcfOptions>(Configuration.GetSection("wcfOptions"));
var globalAuthFilter = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
services.AddMvc(options => { options.Filters.Add(new AuthorizeFilter(globalAuthFilter)); })
.AddJsonOptions
(
options => options.SerializerSettings.ContractResolver = new DefaultContractResolver()
);
// #endregion ASP
// Creating the UnityServiceProvider
var unityServiceProvider = new UnityServiceProvider();
IUnityContainer container = unityServiceProvider.UnityContainer;
// Adding the Controller Activator
// Caution!!! Do this before you Build the ServiceProvider !!!
services.AddSingleton<IControllerActivator>(new UnityControllerActivator(container));
//Now build the Service Provider
var defaultProvider = services.BuildServiceProvider();
// Configure UnityContainer
// #region Unity
//Add the Fallback extension with the default provider
container.AddExtension(new UnityFallbackProviderExtension(defaultProvider));
// Register custom Types here
container.RegisterType<ITest, Test>();
container.RegisterType<HomeController>();
container.RegisterType<AuthController>();
// #endregion Unity
return unityServiceProvider;
}
Since I learned most of what I know about DI in the past week I hope I didnt break any big Pricipal/Pattern if so please tell me!
For ASP.Net Core 2.0, 2.1, 2.2, 3.1 and Unity there is official solution available from Unity authors as NuGet package here: NuGetPackage
Here is Git repository with samples: Git repo
Usage is very simple (from Git repo homepage):
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseUnityServiceProvider() <---- Add this line
.UseStartup<Startup>()
.Build();
And here is example with Unity DI for ASP.Net Core.
I am using this solution in my ASP.Net Core application and works good.

Structuremap mvc 5 injecting applicationdbcontext

Adding Structuremap MVC 5 to an ASP.NET MVC project. I would like to have a singleton of my database connection per request - my controllers would share the same database connection. I am implementing the repository pattern here and need each controller to have a copy of its respective repository. I know this is possible but I think I'm missing or mis-interpretting something wrong.
I have a controller, "Bag," that needs a "IBagRepo"
public class BagController : Controller
{
private readonly IBagRepo repo;
public BagController(IBagRepo repo)
{
this.repo = repo;
}
// actions
}
My first attempt was hooking the singleton database connection in the ControllerConvention, as I assume its called once
public class ControllerConvention : IRegistrationConvention {
public void Process(Type type, Registry registry) {
if (type.CanBeCastTo<Controller>() && !type.IsAbstract) {
// Tried something like
registry.For(type).Singleton().Is(new ApplicationDbContext()); // this
registry.For(type).LifecycleIs(new UniquePerRequestLifecycle());
}
}
}
But it came clear that this isn't the right file to make this change. I went into the registry class that was automatically generated upon installing the nuget package and tried fiddling around with this.
public class DefaultRegistry : Registry {
#region Constructors and Destructors
public DefaultRegistry() {
Scan(
scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
// httpContext is null if I use the line below
// For<IBagRepo>().Use<BagRepo>().Ctor<ApplicationDbContext>().Is(new ApplicationDbContext());
}
#endregion
}
I haven't seen a problem like this out here yet. Am I passing in the right types within my DefaultRegistry class?
What you're wanting is effectively the default behavior if you had been using the StructureMap.MVC5 nuget: https://www.nuget.org/packages/StructureMap.MVC5/. As long as your DbContext is registered with the default lifecycle, that package is using a nested container per http request which effectively scopes a DbContext to an HTTP request for unit of work scoping.
Different tooling than MVC & EF, but I described similar mechanics for FubuMVC + RavenDb w/ StructureMap in this blog post: http://jeremydmiller.com/2014/11/03/transaction-scoping-in-fubumvc-with-ravendb-and-structuremap/
I ended overriding the default controller factory and not using structuremap

Categories