-
-
Notifications
You must be signed in to change notification settings - Fork 838
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AsImplementedInterfaces not Binding all Interfaces #1277
Comments
Running in a debug context or manually executing a registration causes the assembly binder to load some things that may not already be loaded. We silently ignore types we can't load. Do something like this and see what you get: using System.Diagnostics;
using System.Linq;
using Autofac.Util;
var applicationAssemblies = LoadApplicationAssemblies("squad.");
var loadable = applicationAssemblies.SelectMany(a => a.GetLoadableTypes()).ToArray();
Debug.WriteLine("LOADABLE TYPES:");
foreach (var t in loadable)
{
Debug.WriteLine($"- {t.FullName}");
} (Not writing this with a compiler, just trying to get help out fast. Point being, list the actual set of loadable types.) Look in the list and see if both the type you want registered and the interface are in the list. My guess is that one or both are not, and that the debugger (or the compiler, when you manually register) is what's doing the loading transparently. |
I've added this code snipped and the Type I want to bind is listed LOADABLE TYPES:
the two bold ones I want to bind are not binded. |
I should have read the exception closer. See this part in the stack?
That means it's not going through Autofac at all to resolve stuff. Unless there's some inner exception with Autofac info that you've omitted, it's looking like the problem isn't that Autofac can't resolve the type; it's that you're not even going through Autofac to get there. |
How can this happen? When I add it manually it works and when I don't add it its not using autofac for this interface to bind. |
Can you provide your Program.cs/Startup.cs configuration? Or an example repo with a very minimal reproduction? Just to check that you aren't somehow 'building' your container with Autofac, but then not using Autofac for the actual resolve operation. |
I've added the zipfile with my small demo project in the initial post, is that enough?
|
Aside: The zip file might be easier to work with if it was more of a minimal repro thing. It's got the whole git repo, all the |
The project as provided in the zip file doesn't build. On build I see:
Looking at The
I further see that the I'm not going to spend any more time debugging this. My current guess is that there are stale versions of libraries being used and/or cached and the problem is that while the controller is looking for I'm not sure if other contributors have additional time to spend here, but I won't personally be able to come back until this is:
I legitimately do apologize for having to kick it back to you like this. I do want to help, but while we'll do our best to troubleshoot stuff that has to do with Autofac, we're not a free consulting and debugging service. You'll have to do some work, too, if you need help. |
thanks for your time, I will do my best to break this down into minimal repo i could host on github to reproduce the issue. |
I've created the smales example possible and got the same problem I've also checked the Loadable Types example from above and there are all types included. Here is the link to the git Repo https://github.com/squadwuschel/autofacTest.git its an .NET Core 3.1 WebApi project. Just hitting F5 in VS 2019 should compile it and starting browser and showing the error like above. |
The problem is that the types you're registering aren't the same as the types the controller is referring to. This seems counter-intuitive, but it has to do with .NET assembly load context. In a nutshell, there are some assemblies already loaded into the application by the .NET assembly loader. Later, your code is trying to load the same assemblies but using Here's some code you can put in // There are some assemblies already loaded.
var appDomainAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.Contains("squad", StringComparison.OrdinalIgnoreCase));
Debug.WriteLine("ASSEMBLIES ALREADY LOADED:");
foreach (var a in appDomainAssemblies)
{
Debug.WriteLine($"- {a.FullName}");
}
// Grab the assembly already loaded in the AppDomain.
var alreadyLoadedAssembly = appDomainAssemblies.First(a => a.GetName().Name.Equals("Squad.AutofacTest", StringComparison.OrdinalIgnoreCase));
Debug.WriteLine($"ALREADY LOADED: {alreadyLoadedAssembly.FullName} | {alreadyLoadedAssembly.CodeBase}");
// Some assemblies get loaded manually.
var applicationAssemblies = LoadApplicationAssemblies("squad.");
Debug.WriteLine("ASSEMBLIES LOADED BY LoadApplicationAssemblies:");
foreach (var a in applicationAssemblies)
{
Debug.WriteLine($"- {a.FullName}");
}
// Grab the assembly just now loaded in a different context using Assembly.LoadFile.
var justLoadedAssembly = applicationAssemblies.First(a => a.GetName().Name.Equals("Squad.AutofacTest", StringComparison.OrdinalIgnoreCase));
Debug.WriteLine($"JUST LOADED: {justLoadedAssembly.FullName} | {justLoadedAssembly.CodeBase}");
// These two assemblies are NOT EQUAL.
Debug.WriteLine($"LOADED EQUAL? {alreadyLoadedAssembly == justLoadedAssembly}");
// Let's compare types.
var alreadyLoadedType = alreadyLoadedAssembly.GetTypes().First(t => t.Name.Equals("ITodoModelBuilder", StringComparison.OrdinalIgnoreCase));
var justLoadedType = justLoadedAssembly.GetTypes().First(t => t.Name.Equals("ITodoModelBuilder", StringComparison.OrdinalIgnoreCase));
Debug.WriteLine($"ALREADY LOADED: {alreadyLoadedType.FullName}");
Debug.WriteLine($"JUST LOADED: {justLoadedType.FullName}");
Debug.WriteLine($"ARE THOSE THE SAME TYPE? {alreadyLoadedType == justLoadedType}"); The output of this looks like:
This is why the manual registration works - when you have the manual registration... builder.RegisterType<TodoModelBuilder>().As<ITodoModelBuilder>(); ...the compiler is binding this to the same type as the one in the constructor for the controller - they're all in the same assembly. When you do the assembly loading and registering, the registration is literally a different type, at least according to the runtime type loader. It's a "ghost" sort of, where it has the same name but it's not literally the same bytes loaded into memory so it's not the same type. You can further prove this is the case by changing the assembly scanning registration to look like this: var applicationAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.Contains("squad", StringComparison.OrdinalIgnoreCase));
builder.RegisterAssemblyTypes(applicationAssemblies.ToArray()).AsImplementedInterfaces(); That is, if you use the stuff that's already loaded instead of using the Assembly loading problems are hard. They're even harder in .NET Core where dependencies don't actually always come from the folder where the application is compiled/running. We have an issue about trying to write some docs to help a little with this but assembly loading isn't an Autofac concern; it's .NET and the app developer. I would recommend:
However, at least for this issue, Autofac is functioning as designed so I'm going to close it. |
In general as a recommendation, don't use Instead maintain a list of assemblies you want to load types from, something like this: var types = new Assembly[] { typeof(MyClass).Assembly, typeof(MyOtherClass).Assembly }; This requires you to maintain the list, yes, but it also makes sure that you only load what you want. Sometimes explicitness works just better than implicitness. |
Describe the Bug
I am using autofac 6.2.0 in a .NET Core 3.1 web api Project.
I am using the AsImplementedInterfaces implementation to autobind all my Interfaces to my Classes.
The Problem is, that only the (2) ITodoItemManager binding ist working with the AsImplementedInterfaces but the (1) ITodoModelBuilder is not getting bindet and I get a error.
I've debugged it so far, that i can tell that all Assemblies are loaded where the interfaces and classes are included what I am trying to autobind.
Code for autobinding
when I manualy register the ITodoModeBuilder its working
Steps to Reproduce
I'Ve added my small demo Solution with the Problem to repoduce the issue.
WebTemplate.zip
Just set the webproject as start project and you should see the following error
Expected Behavior
All Interfaces should be autobinded with AsImplementedInterfaces when all assemblies are added right.
Exception with Stack Trace
Dependency Versions
Autofac:
Additional Info
The text was updated successfully, but these errors were encountered: