admin管理员组

文章数量:1023204

I have the following registration code:

private static IContainer BuildContainer()
{
    var builder = new ContainerBuilder();
    // current assembly
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

...
    builder.Register(c => new UserSettings(_connectionString)).As<IUserSettings>().**InstancePerLifetimeScope**();
...
    var container = builder.Build();

    **CommonServiceLocator.ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container));**

    return container;
}

I use also a Microsoft's CommonServiceLocator for one edge case. Inside "OnActionExecuting" function I set up one of the properties in IUserSettings:

public override void OnActionExecuting(HttpActionContext actionContext)
{
    ...

    var userSettings = actionContext.Request.GetDependencyScope().GetService(typeof(IUserSettings)) as IUserSettings;

    **userSettings.Test = 123;**
}

In the whole MVC app and inside some libraries, which are MVC app dependencies I can resolve the same object of IUserSettings - if I use a classic DI via constructor.But when I try to resolve it via global service locator:

var settings = ServiceLocator.Current.GetInstance<IUserSettings>();

I get a new instance of IUserSettings, not the initial one which come with the request. Singleton instances works fine, but not those registered as "InstancePerLifetimeScope".

Does anybody know what I did wrong? Is there a way to resolve the same instance of IUserSettings using global "ServiceLocator"?

I have the following registration code:

private static IContainer BuildContainer()
{
    var builder = new ContainerBuilder();
    // current assembly
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

...
    builder.Register(c => new UserSettings(_connectionString)).As<IUserSettings>().**InstancePerLifetimeScope**();
...
    var container = builder.Build();

    **CommonServiceLocator.ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container));**

    return container;
}

I use also a Microsoft's CommonServiceLocator for one edge case. Inside "OnActionExecuting" function I set up one of the properties in IUserSettings:

public override void OnActionExecuting(HttpActionContext actionContext)
{
    ...

    var userSettings = actionContext.Request.GetDependencyScope().GetService(typeof(IUserSettings)) as IUserSettings;

    **userSettings.Test = 123;**
}

In the whole MVC app and inside some libraries, which are MVC app dependencies I can resolve the same object of IUserSettings - if I use a classic DI via constructor.But when I try to resolve it via global service locator:

var settings = ServiceLocator.Current.GetInstance<IUserSettings>();

I get a new instance of IUserSettings, not the initial one which come with the request. Singleton instances works fine, but not those registered as "InstancePerLifetimeScope".

Does anybody know what I did wrong? Is there a way to resolve the same instance of IUserSettings using global "ServiceLocator"?

Share Improve this question edited Nov 19, 2024 at 17:13 Uwe Keim 40.8k61 gold badges190 silver badges304 bronze badges asked Nov 19, 2024 at 13:12 Marcin WasikMarcin Wasik 1128 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

The short version is: You can't [easily] do what you're trying to do.

If you recall from your lifetime scope knowledge, lifetime scopes in a request-based application are hierarchical.

  • Root container / root lifetime scope - ServiceLocator is usually stuck here.
    • Request 1 lifetime scope - this is where controller parameters come from.
    • Request 2 lifetime scope
    • ...
    • Request N lifetime scope

If you register something as InstancePerLifetimeScope, it's going to create one in each of those "buckets."

  • If you resolve from the root (like what ServiceLocator does), you'll get an instance there.
  • If you resolve from a request lifetime (like the constructor in a controller), you'll get an instance from that scope.

But there's no "tie" between the ServiceLocator and a request lifetime scope.

Now, the AutofacDependencyResolver does have a Current property to let you get the request lifetime scope, but be aware that's only going to work in the context of a web request - if you do something async and the request ends before your work is done, that resolver will disappear out from under you.

DI is largely "viral." If you switch from DI to service location, you're sort of breaking the pattern and it's going to require some workarounds and hackery. I know sometimes that's required, just, you're not going to get as much "free out of the box" and "just working" when you switch from one to the other, which is, apparently, what's happening. You'd be better off if you can stick with DI and allow request services to be injected as needed and not go through service location if possible.

I have the following registration code:

private static IContainer BuildContainer()
{
    var builder = new ContainerBuilder();
    // current assembly
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

...
    builder.Register(c => new UserSettings(_connectionString)).As<IUserSettings>().**InstancePerLifetimeScope**();
...
    var container = builder.Build();

    **CommonServiceLocator.ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container));**

    return container;
}

I use also a Microsoft's CommonServiceLocator for one edge case. Inside "OnActionExecuting" function I set up one of the properties in IUserSettings:

public override void OnActionExecuting(HttpActionContext actionContext)
{
    ...

    var userSettings = actionContext.Request.GetDependencyScope().GetService(typeof(IUserSettings)) as IUserSettings;

    **userSettings.Test = 123;**
}

In the whole MVC app and inside some libraries, which are MVC app dependencies I can resolve the same object of IUserSettings - if I use a classic DI via constructor.But when I try to resolve it via global service locator:

var settings = ServiceLocator.Current.GetInstance<IUserSettings>();

I get a new instance of IUserSettings, not the initial one which come with the request. Singleton instances works fine, but not those registered as "InstancePerLifetimeScope".

Does anybody know what I did wrong? Is there a way to resolve the same instance of IUserSettings using global "ServiceLocator"?

I have the following registration code:

private static IContainer BuildContainer()
{
    var builder = new ContainerBuilder();
    // current assembly
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

...
    builder.Register(c => new UserSettings(_connectionString)).As<IUserSettings>().**InstancePerLifetimeScope**();
...
    var container = builder.Build();

    **CommonServiceLocator.ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container));**

    return container;
}

I use also a Microsoft's CommonServiceLocator for one edge case. Inside "OnActionExecuting" function I set up one of the properties in IUserSettings:

public override void OnActionExecuting(HttpActionContext actionContext)
{
    ...

    var userSettings = actionContext.Request.GetDependencyScope().GetService(typeof(IUserSettings)) as IUserSettings;

    **userSettings.Test = 123;**
}

In the whole MVC app and inside some libraries, which are MVC app dependencies I can resolve the same object of IUserSettings - if I use a classic DI via constructor.But when I try to resolve it via global service locator:

var settings = ServiceLocator.Current.GetInstance<IUserSettings>();

I get a new instance of IUserSettings, not the initial one which come with the request. Singleton instances works fine, but not those registered as "InstancePerLifetimeScope".

Does anybody know what I did wrong? Is there a way to resolve the same instance of IUserSettings using global "ServiceLocator"?

Share Improve this question edited Nov 19, 2024 at 17:13 Uwe Keim 40.8k61 gold badges190 silver badges304 bronze badges asked Nov 19, 2024 at 13:12 Marcin WasikMarcin Wasik 1128 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

The short version is: You can't [easily] do what you're trying to do.

If you recall from your lifetime scope knowledge, lifetime scopes in a request-based application are hierarchical.

  • Root container / root lifetime scope - ServiceLocator is usually stuck here.
    • Request 1 lifetime scope - this is where controller parameters come from.
    • Request 2 lifetime scope
    • ...
    • Request N lifetime scope

If you register something as InstancePerLifetimeScope, it's going to create one in each of those "buckets."

  • If you resolve from the root (like what ServiceLocator does), you'll get an instance there.
  • If you resolve from a request lifetime (like the constructor in a controller), you'll get an instance from that scope.

But there's no "tie" between the ServiceLocator and a request lifetime scope.

Now, the AutofacDependencyResolver does have a Current property to let you get the request lifetime scope, but be aware that's only going to work in the context of a web request - if you do something async and the request ends before your work is done, that resolver will disappear out from under you.

DI is largely "viral." If you switch from DI to service location, you're sort of breaking the pattern and it's going to require some workarounds and hackery. I know sometimes that's required, just, you're not going to get as much "free out of the box" and "just working" when you switch from one to the other, which is, apparently, what's happening. You'd be better off if you can stick with DI and allow request services to be injected as needed and not go through service location if possible.

本文标签: