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?
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);
}
}
)
);
}
}