Mohammad Bigdeli
Mohammad Bigdeli

Reputation: 127

How do you resolve a per-request item using service location in ASP.NET Core 3.1 and Autofac?

I have used this snippet to setup my application:

 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())
                .ConfigureContainer<ContainerBuilder>(Startup.Register)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
  public static void Register(ContainerBuilder builder)
        {
            builder.RegisterType<UserService>().As<IUserServcice>().InstancePerLifetimeScope();
        }

and I have utilized it in the manner mentioned below:

  public interface IUserServcice
    {
        public long Tick { get; } 
    }

    public class UserService : IUserServcice
    {
        private long _tick;
        public UserService()
        {
            _tick = DateTime.Now.Ticks;
        }


        public long Tick => _tick;
    }




   public WeatherForecastController(IUserServcice userServcice)
        {
          //  _logger = logger;
            iUserServcice = userServcice;
            var g = Startup.AutofacContainer.Resolve<IUserServcice>();
            tick2 = g.Tick;
        }

        private async Task Get1()
        {
            var list = new List<long>();
            list.Add(iUserServcice.Tick);
            var g=Startup.AutofacContainer.Resolve<IUserServcice>(); 
            list.Add(g.Tick);
            list.Add(tick2);
            //using (var scope= SorviceLocator.Container.BeginLifetimeScope("t1"))
            //        {
            for (int i = 0; i < 3; i++)
            {
                await Task.Factory.StartNew(() =>
                {
                 
                        var sr = Startup.AutofacContainer.Resolve<IUserServcice>();
                        list.Add(sr.Tick);
                  
                 
                });
            }

              //      }
         
        }

        [HttpGet]
        public async Task<IEnumerable<WeatherForecast>> Get()
        {
            await Get1();

            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }

and unfortunately the result of debug is like image uploaded below: enter image description here

as you can see in the picture item in the top is the result of controller constructor injection and, other items, are inside the controller and my question is that how can I have all these items with the same value.

Upvotes: 1

Views: 363

Answers (1)

Travis Illig
Travis Illig

Reputation: 23924

When you use ASP.NET Core, while you can have Autofac be the backing container, for the most part you give up working with Autofac directly when outside the Startup class. You register your stuff in Startup, but in controllers and elsewhere, it's all standard dependency injection (no Autofac references) and the Microsoft dependency injection abstraction.

This is important because it'll help you Google for answers. Don't look for "How do I do this with Autofac?" - look for "How do I do this in ASP.NET Core?"

First, avoid service location. I see what you're doing, I see what you're getting at... but the fact you need to use service location to demonstrate the issue seems like a red flag.

Now that's out of the way:

What you want is HttpContext.RequestServices. When you have a controller, you'll have the HttpContext and the RequestServices object there is the request lifetime scope. It's backed by Autofac but the interface is the Microsoft interface.

You can read about RequestServices in the Microsoft docs.

private readonly IUserService injected;

public WeatherForecastController(IUserService userService)
{
  this.injected = userService;
}

public async Task Get()
{
  var located = this.HttpContext.RequestServices.GetService<IUserService>();
  // located and injected will be the same instance.
}

If you need to begin a child lifetime scope, again, that's an MS DI thing. You'll need an IServiceScopeFactory. That can be a constructor dependency or you can use service location like you were doing before.

var scopeFactory = this.HttpContext.RequestServices.GetService<IServiceScopeFactory>();
using(var scope = scopeFactory.CreateScope())
{
  // Now you have a scope to work with.
}

If you absolutely must get the Autofac lifetime from an IServiceProvider for whatever reason, you can resolve one. Resolving a lifetime scope from a lifetime scope returns itself.

var requestScope = this.HttpContext.RequestServices.GetService<ILifetimeScope>();

But, again, you'll notice everything we're doing here is working with the Microsoft DI abstraction, so when you're looking for answers, I'd recommend looking more broadly and not limiting your search to Autofac. This answer is basically the same regardless of the backing container you use.

Upvotes: 2

Related Questions