I realize this question is asked a lot. But I cannot figure out the solution to this problem.
The thing i am trying to resolve is create three seperate interfaces (Singleton Scoped, InstancePerRequest). And register all the services under their implemented interface, without having to manually add them to DI container.
Autofac Scanning Assemblies for certain class type
Autofac register assembly types
Autofac assembly scanning - .NET Core
Before you say duplicate hear me out.
I have provided a solution to the question asked. But i would like to search the whole solution and not be restricted to a project. I have addded my autofac module inside the Services project so it registers what exists inside services projects. Please see answer below for a better understanding.
I have tried to implement a multi project scanning see code below. It does not work.
Here is my Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Here is my Configure container in my Startup Class. I have left out all others since this is just a new ASP.NET Core Project.
public void ConfigureContainer(ContainerBuilder builder)
{
//builder.RegisterType<EpisodeServices>().As<IEpisodeService>().InstancePerLifetimeScope(); This works
var executingDirectory = AppDomain.CurrentDomain.BaseDirectory;
//The sth is my Solution's header for example Sth.Core, Sth.Models, Sth.Services all are childs to the Sth Solution
string[] files = Directory.GetFiles(executingDirectory, "Sth.*.dll", SearchOption.AllDirectories);
var listOfAssemblies = new List<Assembly>();
foreach (var file in files)
listOfAssemblies.Add(Assembly.LoadFile(file));
builder
.RegisterAssemblyTypes(listOfAssemblies.ToArray())
.Where(t => t.GetInterfaces().Any(i => i.IsAssignableFrom(typeof(ISthScopedService))))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
So far so good.
Here is an example Service so you can fully see my implementation.
public class EpisodeServices : IEpisodeService
{
public IList<Episode> GetEpisodes()
{
return new List<Episode>
{
new Episode { Id = 1, Name = "Some Name", Description = "Some Description" }
};
}
}
And here is the interface:
public interface IEpisodeService : ISthScopedService
{
IList<Episode> GetEpisodes();
}
Here is the injection to the controller
public class EpisodeController : Controller
{
private readonly IEpisodeService _episodeService;
public EpisodeController(IEpisodeService episodeService)
{
_episodeService = episodeService;
}
public IActionResult Index()
{
var data = _episodeService.GetEpisodes();
return Content(data[0].Name);
}
}
If i run this like this i get an Invalid Operation Exception: Unable to resolve service for type namespace.IEpisodeService
while attempting to activate EpisodeController
.
Could someone provide more implementation details on how to achieve this?
The solution provided does not resolve solution scanning. But it uses assembly scanning. You may as well view this as a step-by-step guide on how to achieve this. Our Solution Looks sth like this.
Sth.Core --> Class Library.
Sth.Services --> Class Library(Assembly scanning will happen here).
Sth.Web --> ASP.NET Core MVC project.
The Sth.Core has our three-lifetime interfaces which our Services need to inherit from.
- ISthInstanceService
- ISthScopedService
- ISthSingletonService
Sth.Web.Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory()) //Add this
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Sth.Web,Startup.cs
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new SthModule()); // This is the autofac Module. We add it later at the Services Project
}
Now for our Services Project.Here we need to create our autofac module. The one asp.net app instantiated.
public class SthModule : Module
{
protected override void Load(ContainerBuilder builder)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
builder
.RegisterAssemblyTypes(assemblies)
.Where(t => t.GetInterfaces().Any(i => i.IsAssignableFrom(typeof(ISthScopedService))))
.AsImplementedInterfaces()
.InstancePerLifetimeScope(); // Add similar for the other two lifetimes
base.Load(builder);
}
}
The above code will scan all the Services project and add all the classes that inherit from the ISthScopedService and register them to the container.
Sth.Service.EpisodeService.cs
public class EpisodeServices : IEpisodeService
{
public IList<Episode> GetEpisodes()
{
return new List<Episode>
{
new Episode { Id = 1, Name = "Imposter Syndrome", Description = "Imposter syndrome" }
};
}
}
And the interface.
public interface IEpisodeService : ISthCommonService
{
IList<Episode> GetEpisodes();
}
Now we have implemented assembly scanning(auto-registration) for our services project.