Nesting dependency with "design area" - autofac

I am using autofac 3.5.x and I have the same setup:

public class ServiceA : IServiceA { }
public class ServiceB : IServiceB { public ServiceB(IServiceA sa) { } }
public class ServiceC : IServiceC { public ServiceC(IServiceA sa) { } }
public class ServiceD : IServiceD { public ServiceD(IServiceA sa, IServiceB sb, IServiceC sc) {} }

      

In my container, I have the following registrations:

builder.RegisterType<ServiceA>.As<IServiceA>();
builder.RegisterType<ServiceB>.As<IServiceB>();
builder.RegisterType<ServiceC>.As<IServiceC>();
builder.RegisterType<ServiceD>.As<IServiceD>();

      

Here's what I want: When I request a new one IServiceD

from the container, I want the same instance to IServiceA

be injected into IServiceD

and its dependencies IServiceB

and IServiceC

. However, I'm not looking for the global singles area. The next time I ask for an instance IServiceD

, it should be instantiated with a new instance IServiceA

(I know you cannot instantiate an interface, but I think you get the point).

To illustrate; when i ask the autofac container for IServiceD

i want the following:

public class ServiceD : IServiceD
{
    public ServiceD(IServiceA sa, IServiceB sb, IServiceC sc)
    {
        // SA should be the same (but it is not)
        sa.GetHashCode() == sb.GetServiceA().GetHashCode() == sc.GetServiceA().GetHashCode()
    }
}

      

Please note that the method is GetServiceA()

included only to illustrate my point.

So, I think I am looking for a way to tell autofac that when it resolves IServiceD

, it should create a singleton from ServiceA

, but only for the design area ServiceD

.

I am currently using autofacs support for delegate factories and ask:

public ServiceD(IServiceA sa, Func<IServiceA, IServiceB> fb, Func<IServiceA, IServiceC> fc) : IServiceD
{
     var sb = fb(sa); // Manually inject the same instance of ServiceA
     var sc = fc(sa); // Manually inject the same instance of ServiceA

     // ServiceA is now the same instance
     sa.GetHashCode() == sb.GetServiceA().GetHashCode() == sc.GetServiceA().GetHashCode()
}

      

It keeps me going, but I have a feeling it could be done better - which is why I am reaching out to the experts now.

Thanks in advance.

+3


source to share


1 answer


InstancePerLifetimeScope

can meet your needs. It allows you to have one copy for the entire life span.

builder.RegisterType<ServiceA>().As<IServiceA>().InstancePerLifetimeScope();
builder.RegisterType<ServiceB>().As<IServiceB>().InstancePerLifetimeScope();
builder.RegisterType<ServiceC>().As<IServiceC>().InstancePerLifetimeScope();
builder.RegisterType<ServiceD>().As<IServiceD>().InstancePerLifetimeScope();

      

Then, each time you resolve IServiceD

in a new scope, you will have one instanceIServiceA

using (ILifetimeScope scope = container.BeginLifetimeScope())
{
    // only an instance of IServiceA will be created for this scope
    scope.Resolve<IServiceD>(); 
}

      

If you cannot create a new one ILifetimeScope

, you can use a type Owned<T>

that is a lightweight lifetimecope.

using (Owned<IServiceD> ownedD = container.Resolve<Owned<IServiceD>>())
{
    IServiceD serviceD = ownedD.Value;                 
}

      



Each time you allow Owned<IserviceD>

a new one ILifetimeScope

will be created and only one will be created IServiceA

.

If that's not enough, you can play with InstancePerMatchingLifetimeScope

, but it will make your code and logging rather difficult to read and debug. InstancePerLifetimeScope

should be enough.

Another solution is to use this type of registration.

builder.Register(ctx =>
{
    IServiceA serviceA = ctx.Resolve<IServiceA>();
    IServiceB serviceB = ctx.Resolve<IServiceB>(TypedParameter.From(serviceA));
    IServiceC serviceC = ctx.Resolve<IServiceC>(TypedParameter.From(serviceA));
    IServiceD serviceD = ctx.Resolve<IServiceD>(TypedParameter.From(serviceA), TypedParameter.From(serviceB), TypedParameter.From(serviceC));
    return serviceD;
}).As<IServiceD>();

      

But actually not very elegant.

+2


source







All Articles