0

At first I did confidentially suppose that I could understand it, but via some simple example with Autofac, it appeared that I might understand it wrong, here is the code that I've tried:

//register the service
autofacBuilder.RegisterType<MyService>()
              .As<IMyService>().InstancePerLifetimeScope();
//testing code
void _test1()
{
   var myService = autofacContainer.Resolve<IMyService>();
}
void _test2()
{
    _test1();
    var myService = autofacContainer.Resolve<IMyService>();
}

Test it by running _test2() and you can simply check the instances resolved in the 2 methods.

So with the code above, I understand the myService in _test1 and myService in _test2 should be different. Because I think the lifetime scope of myService in _test1 should be just in that method while the lifetime scope of myService in _test2 should be in _test2 as well. So we have 2 different scopes here, but somehow the resolved instances of myService are the same one.

So could you please explain that issue to me, what does lifetime scope exactly mean here? inside one same class? or something even larger?

halfer
  • 19,824
  • 17
  • 99
  • 186
Hopeless
  • 4,397
  • 5
  • 37
  • 64
  • Well you're not creating a nested lifetime scope so I would expect the instances to be the same. – DavidG Jun 01 '18 at 13:12
  • Lifetime of the container, I believe. The container knows nothing of your curly brackets. It is rooted and not collected, and has a reference to the MyService instance it is managing. So, every time you ask the same container for an instance of that type, you're getting the same instance (due to your configuration). –  Jun 01 '18 at 13:13
  • @DavidG so do you mean that we need to declare the scope for it to work? I think it can detect the scope automatically for me. Without declaring any scope, the `.InstancePerLifetimeScope()` works much like `.SingleInstance()`? – Hopeless Jun 01 '18 at 13:16
  • @Will really what you said confuses me about `.InstancePerLifetimeScope()` and `.SingleInstance()`. – Hopeless Jun 01 '18 at 13:17
  • 1
    Basically yes, you need to manually create a scope otherwise you have a singleton. See here: http://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-lifetime-scope – DavidG Jun 01 '18 at 13:17
  • @DavidG so its usage is a bit confusing, without any scope declared, there should be an exception thrown (when resolving), if not wanting the exception and still want to use it like a singleton, then use `.SingleInstance()` instead. – Hopeless Jun 01 '18 at 13:33
  • No, because Autofac has a global static scope. – DavidG Jun 01 '18 at 13:34
  • The docs are pretty clear. Single instance apparently spans all lifetime scopes. InstancePerLifetimeScope means that when you create scopes (which you aren't doing), an instance's lifespan is limited to that scope. Look at the docs here http://docs.autofac.org/en/latest/lifetime/instance-scope.html#instance-per-lifetime-scope See how they're creating a new scope? You're not doing it here. –  Jun 01 '18 at 13:52

1 Answers1

2

You're confusing c# scopes and autofac's scopes. It's like comparing apples and a fence. :) They are just different and have nothing to do with each other.

So, to clarify it please look at basic examples below. Please pay attention that the scopes should actually be destroyed by you if you are the one who started them as it is done in example 1. In other examples I skipped that for brevity.

// example 1
autofacBuilder.RegisterType<MyService>().As<IMyService>().InstancePerLifetimeScope();
var container = autofacBuilder.Build();

void _test1(IComponentContext scope){
    var myService = scope.Resolve<IMyService>();
}

void _test2(IComponentContext scope){
    // the same scope is used here and in _test1()
    // this means that the service instance will be the same here and there
    _test1(scope);
    var myService = scope.Resolve<IMyService>();
}

// it's only here that DI lifetime scope starts
using (var scope = container.BeginLifetimeScope()) {
    _test2(scope);
}



// example 2
autofacBuilder.RegisterType<MyService>().As<IMyService>().InstancePerLifetimeScope();
var container = autofacBuilder.Build();

void _test1(IComponentContext scope){
    var myService = scope.Resolve<IMyService>();
}

void _test2(IComponentContext scope){
    // now new scope is used in _test1() call
    // this means that instances will be different here and there since they are resolved from different scopes
    _test1(scope.BeginLifetimeScope());
    var myService = scope.Resolve<IMyService>();
}

var scope = container.BeginLifetimeScope();
_test2(scope);



// example 3
// NOTE THIS!
autofacBuilder.RegisterType<MyService>().As<IMyService>().InstancePerDependency();
var container = autofacBuilder.Build();

void _test1(IComponentContext scope){
    var myService = scope.Resolve<IMyService>();
}

void _test2(IComponentContext scope){
    // the same scope is used here and in _test1()
    // but now service instances will be different even though they are resolved from the same scope
    // since registration directs to create new instance each time the service is requested.
    _test1(scope);
    var myService = scope.Resolve<IMyService>();
}

var scope = container.BeginLifetimeScope();
_test2(scope);



// example 4
autofacBuilder.RegisterType<MyService>().As<IMyService>().SingleInstance();
var container = autofacBuilder.Build();

void _test0(IComponentContext scope){
    var myService = scope.Resolve<IMyService>();
}

void _test1(IComponentContext scope){
    _test0(scope.BeginLifetimeScope());
    var myService = scope.Resolve<IMyService>();
}

void _test2(IComponentContext scope){
    // all the scopes used here and in other calls are different now
    // but the service instance will be the same in all of them even though it is requested from different scopes
    // since registration directs to get the same instance each time the service is requested regardless of the lifetime scope.
    _test1(scope.BeginLifetimeScope());
    var myService = scope.Resolve<IMyService>();
}

var scope = container.BeginLifetimeScope();
_test2(scope);
Alexander Leonov
  • 4,694
  • 1
  • 17
  • 25
  • thank you, really I thought it should match the code scope (which would be so wonderful). When using IoC we don't always declare scope explicitly unless you want to put `autoFac` as a tight-coupled component right in your complex services. – Hopeless Jun 01 '18 at 15:22