What is the context of services.AddTransient ?
SherifRefaat opened this issue · comments
Hi, I have the following problem
public void ConfigureServices(IServiceCollection services)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.ToArray();
RegisterRepositories(ref services, assemblies);
}
private void RegisterRepositories(ref IServiceCollection services, Type[] assemblies)
{
var interfaceT = typeof(IRepository);
foreach (var t in assemblies.Where(x => interfaceT.IsAssignableFrom(x) && x != interfaceT))
{
services.AddTransient(t);
}
}
As you can see I am adding my classes that implements from IRepository (marker/empty interface). But the problem is that they don't get registered! I get the following message:
InvalidOperationException: Unable to resolve service for type 'MyProject.Core.Repositories.MenuRepository' while attempting to activate 'MyProject.Controllers.MenuController'.
Which is the my my controller that request a certain repository in its constructor.
But if I changed the code to the following, the DI is able to resolve the instance and activate the controller
public void ConfigureServices(IServiceCollection services)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.ToArray();
// Register Repositories
var interfaceT = typeof(IRepository);
foreach (var t in assemblies.Where(x => interfaceT.IsAssignableFrom(x) && x != interfaceT))
{
services.AddTransient(t);
}
}
I don't see anything obviously wrong in the code you've shown. But I do have a few comments/questions:
- The
ref
keyword isn't needed because theservices
parameter is never set - What does the
MenuController
constructor look like? - What does the
MenuRepository
type definition look like?
@Eilon Here is a repository containing the full code:
https://github.com/SherifRefaat/aspnet-DependencyInjection-Issue-612
@SherifRefaat did you debug the startup code? There are no loaded repository types in the application so nothing gets added to the container.
Assemblies aren't all loaded on startup, they are loaded as they are used. That's likely your problem.
edit: Take a look at
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
You'll see that MyProject.Core isn't in there.
@davidfowl It seems that when the GetAssemblies()
is called the IRepository
was still not referenced hence MyProject.Core
was not loaded. Am I correct?
I changed to the following and it is working
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var projectTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.ToList();
RegisterFromBaseType(services, typeof(IRepository), projectTypes);
}
private void RegisterFromBaseType(IServiceCollection services, Type type, List<Type> allTypes)
{
foreach (var t in allTypes.Where(x => type.IsAssignableFrom(x) && x != type))
{
services.AddTransient(t);
}
}
Also, @davidfowl would this have produced different result on Mono because its runtime has multiple Execution Modes (i.e. interpreter mode)?
@SherifRefaat possibly, relying on loaded assemblies is a mistake specifically because of the non deterministic behavior you're experiencing.
When you do typeof(IRepository)
in ConfigureServices, it will be loaded as part of the method being JITed.