Search code examples
c#.netautofac

Autofac can inject property like @Resource in Spring?


Im using .Net6 with Autofac, now I have a controller and two service:

public class TestController {
    public ITestService TestService { get; set; }
    
    public string Test() => TestService.Test();
}
public interface ITestService {
    string Test();
}

public class TestService1 : ITestService {
    public string Test() => "Test1";
}

public class TestService2 : ITestService {
    public string Test() => "Test2";
}

I register them in Module:

public class AutofacModule : Autofac.Module {
    // use assembly scanning to register: https://autofac.readthedocs.io/en/latest/register/scanning.html
    protected override void Load(ContainerBuilder builder) {
        builder.RegisterControllers().PropertiesAutowired().AsSelf();

        builder.RegisterServices().AsImplementedInterfaces();
    }
}

Now TestController.Get() will return "Test1" because TestService1 was injected. If I want to get TestService2(or other implemented instances), I need use constructor to take it.

In Spring, @Resource("beanName") can specify which instance will be inject, so I want to use an attribute to match instance`s name and inject it like Spring:

public class TestController {
    [Resource("TestService2")]
    public ITestService TestService { get; set; }
    
    // return "Test2"
    public string Test() => TestService.Test(); 
}

How can I do about that?


Solution

  • Autofac.Annotation sloved it.

    In short, it used AutowiredAttribute (extend ParameterFilterAttribute) and pipeline phases, the pseudo-code like this:

    // Autofac.Module
    protected override void Load(ContainerBuilder builder) {
        // get all components that has `[Component]` attribute in process;
        List<Type> components = GetComponents();
        foreach (Type c in components) {
            // register as self
            var registration = builder.RegisterType(c).WithAttributeFiltering();
            
            // get properties that has `[Autowired]` attribute
            List<PropertyInfo> properties = GetProperties(c);
            registration.ConfigurePipeline(pb => 
                pb.Use(PipelinePhase.Activation, MiddlewareInsertionMode.StartOfPhase,
                       // the callback will be execute when components was resolved
                       (context, next) => {
                           next(context);
                           object instance = context.Instance;
                           foreach (PropertyInfo info in properties) {
                               // get component from container by `Autowired`'s options
                               context.TryResolveService(..., out object obj);
                               info.SetValue(intance, obj);
                           }
                       }
                )
            );
        }
    }