-2

I am creating the controller dynamically for the types to be loaded from the dll. For that I created one base controller to handle dynamic controller request.

My dynamic controller has following dependencies in the construcor

public class Order
{
    private readonly IDBLogger _logger;
    private readonly IExceptionHandler _handler
    public Order(IDBLogger logger, IExceptionHandler handler)
    {
        _logger=logger;
        _handler=handler
    }
    public void GetOrderDetail()
    {
       _logger.log(".....");   //Here I am getting null reference as I am not able resolve this _logger type.
        
       /// rest code
     }
}

Since the class is loaded from the dll how can I resolve this.

  • do you mind sharing a bit more detail around how exactly you load types from dll and what DI container you currently use in your project – timur Nov 05 '20 at 04:27
  • By using the below code we can get the types from the dll and can be added to the generic controller using ApplicationFeatureProvider populateFeature() Assembly.LoadFrom(Path.Combine(assemblyFolder, "SampleOrder.dll")); I create instance for order type using Activator.CreateInstance(type). But in the order method call the I used the following type instances IDBLogger,IExceptionHandler and getting null value as I am not able to resolve those dependencies during runtime. – CoolTechie Nov 05 '20 at 05:17
  • next question: why do you instantiate your controllers manually? My understanding is you [register your types](https://github.com/dotnet/aspnetcore/blob/5ff9ed68d1cf6c89d72d27a69b00ed0ecd34daed/src/Mvc/Mvc.Core/src/Controllers/ControllerFeatureProvider.cs#L20) and the framework does instantiation (an DI) for you.. so what's your code around `Activator.CreateInstance(type)`? – timur Nov 05 '20 at 05:45
  • I am got some idea from the following thread to add generic controller. [link](https://stackoverflow.com/questions/36680933/discovering-generic-controllers-in-asp-net-core) But the only difference is on that link the controller types ex: VanityOrderController code is available at compile time but for me I get from some dll. Hence we have no control over instance creation so I need to create manually and dynamically for my "Order" type. Since Order type class has constructor dependencies arguments, I am not able to resolve that. Hope you understand? – CoolTechie Nov 05 '20 at 06:15
  • Any suggestions or thoughts? – CoolTechie Nov 05 '20 at 09:06

2 Answers2

1

I think my assumption of your objective was way off and it actually seems to me you don't need to mess with ApplicationParts or FeatureProviders at all. To me it appears to be a DI registration issue.

  1. I started with boilerplate .net core 3.1 MVC application (nothing fancy there)
  2. then I added two more class library projects: Common and Orders

Common.dll

I assumed that your interfaces (I'll just use IDBLogger for brevity) must be made available to the controller assembly as well as to the main application. So both projects are set to depend on it.

namespace Common
{
    public interface IDBLogger
    {
        void Log(string s);
    }
}

Orders.dll

this assembly defines a web api controller:

using Common;

namespace Orders
{
    public class Order
    {
        private readonly IDBLogger _logger;
        public Order(IDBLogger logger)
        {
            _logger = logger;
        }
        public void GetOrderDetail()
        {
            _logger.Log("Inside GetOrderDetail");
        }
    }
}

MVC

as most of the code is stock standard I'd only list files that I changed. Note, that now I'm relying on default Controller resolver I only need to register my types onto ServiceCollection

Startup.cs

...
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(); // add all controllers as you normally would. I think you don't need to mess with ApplicationParts. but if you do, you are already aware that https://stackoverflow.com/questions/36680933/discovering-generic-controllers-in-asp-net-core is a good example

    services.AddScoped<IDBLogger, IdbLogger>();
    // load assembly and register with DI
    var assembly = Assembly.LoadFrom(Path.Combine("..\\path\\to", "Orders.dll")); 
    var orderType = assembly.ExportedTypes.First(t => t.Name == "Order");
    services.AddScoped(orderType); // this is where we would make our type known to the DI container

    var loadedTypesCache = new LoadedTypesCache(); // this step is optional - I chose to leverage the same DI mechanism to avoid having to load assembly in my controller for type definition. you can probably find a better approach at doing this
    loadedTypesCache.LoadedTypes.Add("order", orderType);
    services.AddSingleton(loadedTypesCache); // singleton seems like a good fit here
}

IdbLogger.cs

using Common;
using System;

namespace Test
{
    public class IdbLogger : IDBLogger
    {
        public void Log(string s)
        {
            Console.WriteLine(s); // nothing fancy here either
        }
    }
}

ValuesController.cs

[Route("api/[controller]")]
[ApiController]
public class ValuesController
{
    public ValuesController(IServiceProvider serviceProvider, LoadedTypesCache cache)
    {
        var order = serviceProvider.GetService(cache.LoadedTypes["order"]); // you could load the same assembly here to get the type again, but i opted to inject a dictionary with preloaded type instead
        
        // following two lines are just to call the method. you probably have better way of doing it
        var m = cache.LoadedTypes["order"].GetMethod("GetOrderDetail", BindingFlags.Public|BindingFlags.Instance);
        m.Invoke(order, new object[] { });

    }

    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new [] { "value1", "value2" };
    }
}

relying on built-in DI container allows us to not think about manually instantiating your dynamically loaded types (and their dependencies as long as they are known to the DI container). You might find that having to use reflection to work with these instances is a bit cumbersome so you might want to explore your options there.

hopefully this is the missing piece

timur
  • 14,239
  • 2
  • 11
  • 32
  • Thanks for the response. Its all most similar code what my project has instead of ValuesController I have Order class from dll which will be added to the controller. I have basecontroller whch gets the Order class type api/order and after creating the instance through Activator.CreateInstance(orderType) I was trying to access its method where I am getting nullreference expection for the logger type.I modified my above code for your quick reference. Though we resolve the logger dependencies in sartup.cs file the it could not resolve for the class from the external dll. – CoolTechie Nov 05 '20 at 12:50
-1

You can dynamically load the dependencies and the controller using reflection. For more info, you can visit the following link. Assemblies

Ramesh Shah
  • 291
  • 2
  • 7
  • It did not resolve the issue again I am getting null reference exception from the method call of Order type. Dependencies were not resolved! – CoolTechie Nov 05 '20 at 09:07