简体   繁体   中英

How to load controllers from different versions of the same assembly

I am trying to solve the following problem and i am not exactly sure how to do it:

I am building a web server that has differenty APIs/Controllers that are loaded from .dll-Files on Startup. It will run in a linux docker container and is implemented as an ASP-NET Webapplication & .net Core 2.1 .

The loading of assemblies that contain controllers works fine by doing something like this:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    services.AddMvc().AddApplicationPart(AssemblyLoadContext.Default.LoadFromAssemblyPath("/PATH/APIPlugin.dll"));
}

This application must have versioned REST-APIs that means: I need to load the same assembly multiple times in different versions. then i need to have some kind of routing between the versions. For example:

  • /api/data/latest/
  • /api/data/v1/

I cannot use AssemblyLoadContext.Default.LoadFromAssemblyPath to load multiple versions of the same assembly. I also tried to grab the controller from the assembly and creating an instance of it like this:

var controllerAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath("/PATH/APIPlugin.dll");
var pluginType = controllerAssembly ExportedTypes.First<Type>();
var pluginInstance = (ControllerBase)Activator.CreateInstance(pluginType);

services.Add(new ServiceDescriptor(pluginType, pluginInstance));

This throws no exception but ultimately does not work. But i am pretty new to ASP.Net so this might very well be nonsense and i would have to find a solutuion to route between the different versions, even if it would work like this.

My Question: How would one approach this requirement ? Is it a god idea/possible to load multiple Controllers from the "same" assembly ? If yes, how would one achieve this? Or would it be a better solution to have one controller that does all the routuing and load some self-defined implementation from the assemblies. So that the controller would route between the versions, and api-methods?

I was able to find a sultion while tinkering around:

public class ControllerPluginProvider : IApplicationFeatureProvider<ControllerFeature>
{
    public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
    {
        var basePath = AppContext.BaseDirectory;
        var pluginPath = Path.Combine(basePath, "plugins");

        foreach (var file in Directory.GetFiles(pluginPath, "*.dll")){
            var assembly = Assembly.LoadFile(file);
            var controllers = assembly.GetExportedTypes().Where(t => typeof(ControllerBase).IsAssignableFrom(t));
            foreach (var candidate in controllers)
            {
                feature.Controllers.Add(candidate.GetTypeInfo());
            }
        }
    }
}

In Startup:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddMvc().ConfigureApplicationPartManager(m => 
  m.FeatureProviders.Add(new ControllerPluginProvider()));
}

This lead to the following error when the same assembly, and therefore a Controller with the same name was loaded: Attribute routes with the same name 'Get' must have the same template

I was able to fix it, and also add versioning with the versioning library: https://github.com/microsoft/aspnet-api-versioning

[ApiVersion("2.0")]
[ApiController]
[Route("api/v{version:apiVersion}/MyController")]
public class MyControllerController : ControllerBase
{
}

Now the only step that is missing, is routing the /latest/ path to the most recent controller of the given type. I have not found a solution to this yet, but this should be doable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM