0

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?

koishi
  • 97
  • 2
  • 7
  • What is your problem with injection through the constructor? – Mohammad Aghazadeh Sep 01 '22 at 05:56
  • @mohammad-aghazadeh No problem, but I should write many lines of code in constructor if I have multiple properties. Use attribute could be more easily and clearly. – koishi Sep 01 '22 at 06:33
  • Check this out, it might help : https://stackoverflow.com/questions/38459625/property-injection-in-asp-net-core#answer-53488056 – Mohammad Aghazadeh Sep 01 '22 at 09:04
  • @Mohammad Thanks, I tried about this. This solution can determine wheather the propery needs to be injected, but not which the instance what will be inject. – koishi Sep 02 '22 at 00:38

1 Answers1

0

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);
                       }
                   }
            )
        );
    }
}
koishi
  • 97
  • 2
  • 7