Autofac automate assembly scanning .net core 3.0 - Register according to Lifetime

放肆的年华 提交于 2020-05-23 21:11:28

问题


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?


回答1:


The solution provided does not resolve solution scanning. But answers the question.

Now you see there is a solution working with very similar code as the above.

But there is a constraint to it. Your interface needs to be in the same project as your builder and base implementation.

You may as well view this as a step-by-step guide on how to achieve this.

Our Solution Looks sth like this.

Three solution projects

  • Sth.Core --> Class Library.

  • Sth.Services --> Class Library.

  • Sth.Web --> ASP.NET Core MVC project.

The Sth.Core has our three lifetime interface which our Services need to inherit from.

Sth.Core

  • ISthInstanceService
  • ISthScopedService
  • ISthSingletonService

All three are empty interfaces.

Sth.Web We need to edit 2 files here. The Program.cs, Startup.cs

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>();
                });
    }

Startup.cs

Add this method to your startup class.

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. That we said inside asp.net app to instantiate.

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 sacn all the Services project and add all the classes that inherit from the ISthScopedService and register them to the container.

Here are our ServiceInstances inside the Service Project.

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.



来源:https://stackoverflow.com/questions/59164555/autofac-automate-assembly-scanning-net-core-3-0-register-according-to-lifetim

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!