0

I am new to .NET Core but I have been using Autofac in ASP.NET and I am trying to register a controller using Autofac configuration as I did in ASP.NET. I read bunch of code examples but it seems like Autofac manual for .NET Core is not working with .NET Core 2.1.

My goal is to load controllers from another project by using Autofac configuration. Is there any working example?

I am using Autofac.Configuration in .NET 4.7 for injecting Controllers as plugins, but I am failing at including Autofac.Configuration in .NET Core 2.1 I have created small ASP.NET Core project and several controllers in other project. The idea is to include those Controllers in ASP.NET Core project by Autofac.Configuration.

Here is my autofac.json config file

{
  "components": [{
    "type": "Controllers.UserController, Controllers",
    "services": [{"type": "Common.IController, Common"}]
  }]
}

Here is code from Startup.cs

public IServiceProvider ConfigureServices(IServiceCollection services)
{
  services.AddMvc().AddControllersAsServices();
  var builder = new ContainerBuilder();
  var mseConfig = new ConfigurationBuilder(); 
  mseConfig.AddJsonFile("autofac.json");
  var module = new ConfigurationModule(mseConfig.Build()); 
  builder.RegisterModule(module);
  builder.Populate(services);
  var container = builder.Build();
  return new AutofacServiceProvider(container);
}

The error I am receiving is "System.InvalidOperationException: 'The type 'Controllers.UserController, Controllers' could not be found. It may require assembly qualification, e.g. "MyType, MyAssembly".'"

Controllers.dll file is present in bin folder of ASP.NET Core project.

Travis Illig
  • 23,195
  • 2
  • 62
  • 85
opejanovic
  • 21
  • 1
  • 3
  • just a question, why not use the provided DI container in .NET Core? – Chirdeep Tomar Oct 06 '18 at 20:26
  • @ChirdeepTomar because native DI doesn't support many features like Property Injection, etc. – Alex Herman Oct 06 '18 at 20:28
  • 1
    @ChirdeepTomar: Because of [reasons](https://stackoverflow.com/questions/30681477/why-would-one-use-a-third-party-di-container-over-the-built-in-asp-net-core-di-c). – Steven Oct 07 '18 at 20:32
  • @ChirdeepTomar - Like Alex Herman said, native DI doesn't support many fancy features. – opejanovic Oct 07 '18 at 23:23
  • 1
    In order to help you with your question we'll need a bit more info. [The "How to ask a question"](https://stackoverflow.com/help/how-to-ask) docs can help you here and some of the stuff [you cross-posted in the issue](https://github.com/autofac/Autofac.Configuration/issues/23) as well: A minimal repro, the full exception (if any) with stack traces, things you've tried, places you've looked for answers. An accurate title would also help - it's not that Autofac.Config isn't working with .NET Core, it's that you can't get a dynamically loaded controller to work. – Travis Illig Oct 08 '18 at 14:44
  • I added an example that may help. I'll post here and in the issue; however, further answers will happen here and NOT in the issue. https://github.com/autofac/Examples/blob/master/src/ConfigurationExample/Program.cs – Travis Illig Oct 08 '18 at 22:01

1 Answers1

0

I have created a working example in the Autofac Examples repo.

The problem lies in how the .NET Core assembly loader does NOT just assume things in your bin folder are loadable. You absolutely need to tell .NET Core's assembly loader where to get assemblies that are not directly referenced. That's not an Autofac config thing, that's .NET Core and it's pretty confusing new loader system.

When Autofac.Configuration tries to load the configured type from an assembly that isn't referenced, the .NET Core loader doesn't recognize the assembly and raises an event that you need to handle so it can work. A simple solution as shown in the example:

AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext context, AssemblyName assembly) =>
{
  // DISCLAIMER: NO PROMISES THIS IS SECURE. You may or may not want this strategy. It's up to
  // you to determine if allowing any assembly in the directory to be loaded is acceptable. This
  // is for demo purposes only.
  return context.LoadFromAssemblyPath(Path.Combine(Directory.GetCurrentDirectory(), $"{assembly.Name}.dll"));
};

The easiest test you can do from your code: If you do Type.GetType("MyNamespace.MyType, MyAssembly") and come out with a null, .NET Core is not finding your type and you need to handle assembly loading.

For more info, you should search for questions/answers on how .NET Core assembly loading works.

UPDATE based on comment thread:

It seems there is some confusion about JSON configuration in combination with assembly scanning style registration of controller registration. There also seems to be some confusion about plugin assembly handling mixed in here. I think the combination of these things makes it appear the answer to the question isn't present, when it actually is... or perhaps that the question being asked isn't clear on what the intent is.

As I read it, there are two things desired:

  • Auto-register controllers in the application assembly.
  • Register additional controllers in an external plugin assembly.

I may already be off-base in my understanding of the question, but that's how it reads, so that's all I can go on.

First, I'd really recommend avoiding plugins if you can. I get that's a really cool thing and probably a feature your app needs, but as noted in my original answer, plugins and assembly loading are really different than how they are in original .NET framework. However, let's just go with it.

When you do something like services.AddControllers() as shown in this example that's going to register all the controllers from the application assembly. It's not going to pick up plugins, as you've seen. That example does work for the controllers in the application assembly and is the replacement for the WebAPI 2 RegisterApiControllers().

Something to note about .NET Core which may not be apparent is that the whole DI system and web application hosting mechanism has been simplified and normalized. There isn't "special treatment" for controllers the way there was in older Web API or ASP.NET MVC. You don't RegisterType<MyController>().As<IController>() or anything like that anymore. You just register whatever types you want. So if you're looking for something like RegisterApiControllers() in Autofac, you're not going to find it. The Autofac ASP.NET Core docs cover a lot of the differences and that can help clarify some of this.

This brings us back to the question about how to register things with configuration. I think the configuration you have is right. I can see maybe you need to not register the controller As anything; or add the type itself, since, again, there's no common controller interface in .NET Core.

{
  "components": [{
    "type": "Controllers.UserController, Controllers",
    "services": [
      {"type": "Common.IController, Common"}
      {"type": "Controllers.UserController, Controllers"}
    ]
  }]
}

Which brings us to the exception. You posted the exception you were seeing and I already explained the cause - the assembly loading mechanism for .NET Core is not nearly as simple as it is in classic .NET. I already pointed you to a working plugin example. There is not a specific example for ASP.NET Core because, again, there's no specific support or differences for ASP.NET Core than there is for any .NET Core console app or anything else. It's all one thing. .NET Core == ASP.NET Core == self-hosted .NET Core services. You just need to handle the assembly loading portion of things in your ASP.NET Core app.

That's all there is in the question. There's nothing else mentioned in the question about assembly scanning (which is not configuration and isn't something you can specify via configuration). There's nothing about using MEF (which is the AddApplicationPart() thing that keeps getting noted in the comment thread).

I'm sorry if this isn't enough to answer what you're trying to accomplish. The question asks how to register a controller via configuration and notes an exception, which I addressed. The comment thread keeps coming back to assembly scanning, which isn't configuration but could be addressed by assembly scanning. The exception notes that an assembly couldn't be found and I explained that assembly loading is complicated and is an orthogonal topic to controller registration and that you should look for more information on .NET Core assembly loading if you need that because it's not something Autofac covers, nor does ASP.NET Core, and there's nothing specific to ASP.NET Core when it comes to assembly loading anyway.

I don't think there's anything else I can provide here, so, again, if that's not enough, I'm sorry.

Travis Illig
  • 23,195
  • 2
  • 62
  • 85
  • Is there an example how to register a controller using autofac configuration file (JSON)? I read https://autofac.readthedocs.io/ but there is no such example :( – opejanovic Jul 27 '21 at 09:30
  • A controller isn't any different than any other type being registered. There are examples of how to register things via configuration; it doesn't have to be a specific example showing _a controller_. However, if the controller is in a different assembly, you'll run into assembly loading challenges like this, and, again, there's nothing special about a _controller_. You'll still have to make sure the assembly loads and that's not something Autofac will do for you. You're in charge of assembly loading. – Travis Illig Jul 27 '21 at 14:01
  • Thanks for your patience. I have no problems registering any other class but since I am missing Autofac.WebApi2 (it not .NET Core compatible) I dont know how to register a controller like I did in .NET FW The only way that cross my mind is like this var assembly = Assembly.LoadFile(path); services.AddControllers().AddApplicationPart(assembly).AddControllersAsServices(); But this is not how It should work. – opejanovic Jul 29 '21 at 10:50
  • Seems like what you're asking is a different question than what's being asked here, or if it's not it at least warrants additional explanation of what you're trying to do and what's not working - show the JSON configuration you've tried, with the controller type you want to register; show the part where you're registering the configuration with the container, etc. Enough that someone could try to replicate what you're doing and see why it's not working. – Travis Illig Jul 29 '21 at 13:57
  • Its the same question as before: how to register a controller using JSON configuration? The first problem was assembly loading (which seems to be solved in .NET 5), but controller registration is still questionable. There is not a single example how to do it, and I am not sure if it is possible. In .NET FW I was relying on Autofac.WebApi2 and the method "RegisterApiControllers", but I cannot use this library or this approach in .NET 5. So the question is: how do you register a controller using JSON config file? – opejanovic Jul 30 '21 at 15:01
  • You asked how to register a controller, I explained that it's not any different than registering any other type. Your configuration looks correct. You asked why you're getting the exception, I explained that it's due to assembly loading issues that you'll have to work out and I gave you an example. You're now asking about `AddControllersAsServices()` and other programmatic mechanisms, which *is not* JSON configuration and is confusing at best. If the above hasn't already answered the question, then I obviously don't understand the question and the comment chain here isn't helping. I'm sorry. – Travis Illig Jul 30 '21 at 15:19
  • Ok, lets try this way: Imagine that you have an ASP.NET 5 web API project and separate library where you have a Controller. You want to add that controller to your ASP.NET app using JSON config. How? The only way I know is to load assembly and register it like this var assembly = Assembly.LoadFile(path); services.AddControllers().AddApplicationPart(assembly).AddControllersAsServices(); But I don't need Autofac for that. – opejanovic Jul 30 '21 at 15:35
  • I've updated the answer to the question based on the comment thread. Obviously what you're asking here isn't making sense to me; or you're asking it in a way that very much makes sense to you but isn't clicking with me. You've mentioned the same thing repeatedly about the AddControllersAsServices() and I'm failing to grasp how that has anything to do with JSON configuration. Hopefully the updated answer helps; if it doesn't, I'm sorry. I don't think I'll be able to spend more time here on this specific question. – Travis Illig Jul 30 '21 at 16:10