ASP.NET CORE Field Injection
- We don't make containers, we are just porters of
NetCore Container
(add some features to the default container). - Don't use
NAutowired
in the constructor. - We have not replaced
NetCore
defaultContainer
, which means you can still add services toContainer
usingIServiceCollection
inStartup
. And useNAutowired
to resolve dependencies. - Spring DI style is ugly(Explicit dependencies), but who cares?
- Introducing
NAutowired
andNAutowired.Core
in the nuget. - The
NAutowired
package should only be referenced in the web project, and theNAutowired.Core
package is referenced in projects that need to add features.
By default, when ASP.NET Core
generates Controller
, dependencies in the Controller
constructor are resolved from the container, but the controller is not resolved from the container, which results in:
- The lifetime of the
Controller
is handled by the framework, not the lifetime of the request - The lifetime of parameters in the
Controller
constructor is handled by the request lifetime - In
Controller
useField Injection
won’t work
You must use the AddControllersAsServices
method to register the Controller
as a Service so that the Controller
can use the Field Injection
when resolve.
Use AddControllersAsServices
in Startup.cs
and replace IControllerActivator
as NAutowiredControllerActivator
.
Replace the default IControllerActivator
implementation with NAutowiredControllerActivator
in Startup.cs
public void ConfigureServices(IServiceCollection services) {
//register controllers as services
services.AddControllers().AddControllersAsServices();
//replace `IControllerActivator` implement.
services.Replace(ServiceDescriptor.Transient<IControllerActivator, NAutowiredControllerActivator>());
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
//Add FooService to container.
services.AddScoped<FooService>();
//Add IBarService implements to container.
services.AddScoped<IBarService, MyBarService1>();
services.AddScoped<IBarService, MyBarService2>();
}
[Route("api/[controller]")]
[ApiController]
public class FooController : ControllerBase {
//Use Autowired injection.
[Autowired]
private readonly FooService fooService;
//Also supports IEnumerable<T> injection.
[Autowired]
private readonly IEnumerable<IBarService> barServices;
[HttpGet]
public ActionResult<string> Get() {
return fooService == null ? "failure" : "success";
}
[HttpPost]
public ActionResult<string> Baz() {
return barServices?.Count > 0 ? "success" : "failure";
}
}
public class Startup {
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
//Add Filter to container.
services.AddScoped<AuthorizationFilter>();
}
}
//Use ServiceFilter like ASP.NET CORE ServiceFilter.
[NAutowired.Attributes.ServiceFilter(typeof(AuthorizationFilter))]
public class FooController : ControllerBase {
}
public class AuthorizationFilter : IAuthorizationFilter {
[Autowired]
private readonly FooService fooService;
public void OnAuthorization(AuthorizationFilterContext context) {
System.Console.WriteLine($"{fooService.ToString()} in filter");
return;
}
}
public class Startup {
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
//add config to ioc container
services.Configure<SnowflakeConfig>(Configuration.GetSection("Snowflake"));
}
}
public class FooController : ControllerBase {
//use autowired get configuration
[Autowired]
private IOptions<SnowflakeConfig> options { get; set; }
[HttpGet("snowflake")]
public IActionResult GetSnowflakeConfig()
{
return Ok(options.Value);
}
}
SnowflakeConfig.cs
public class SnowflakeConfig
{
public int DataCenter { get; set; }
public int Worker { get; set; }
}
appsettings.json
{
"Snowflake": {
"DataCenter": 1,
"Worker": 1
}
}
public class Startup : NAutowired.Core.Startup
{
[Autowired]
private readonly FooService fooService;
//Program start up func
public override void Run(string[] args)
{
System.Console.WriteLine(fooService.Foo());
System.Console.ReadLine();
}
}
class Program
{
static void Main(string[] args)
{
ConsoleHost.CreateDefaultBuilder(new List<string> { "assemblyName" }, args).Build().Run<Startup>();
//You can also let NAutowired use the IServiceCollection you passed
/*
ConsoleHost.CreateDefaultBuilder(() => {
var serviceDescriptors = new ServiceCollection();
serviceDescriptors.AddTransient<FooService>();
return serviceDescriptors;
}, new List<string> { "NAutowiredConsoleSample" }, args).Build().Run<Startup>();
*/
}
}
[Route("api/[controller]")]
[ApiController]
public class FooController : ControllerBase {
//Inject a specific instance.
[Autowired(typeof(FooService))]
private readonly IFooService fooService;
[HttpGet]
public ActionResult<string> Get() {
return fooService == null ? "failure" : "success";
}
}
NAutowired
provides the AutoRegisterDependency(assemblyName)
method for automatic container injection. This way you don't need to add the type to the container one by one in Startup.cs
public class Startup {
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
//services.AddScoped<FooService>();
//Use automatic injection.
services.AutoRegisterDependency(new List<string> { "NAutowiredSample" });
}
}
Use the [Service] [Repository] [Component] [ServiceFilter]
attribute tag class, these classes will be added to the container when AutoRegisterDependency
is executed
//The default Lifetime value is Scoped
[Service]
//Lifetime to choose the life cycle of dependency injection
//[Service(Lifetime.Singleton)]
public class FooService {
}
[Service(implementInterface: typeof(IService))]
//injection interface to container like services.AddScoped(typeof(IService), typeof(FooService));
public class FooService: IService {
}
NAutowired
will automatically scan all classes under the assembly configured by the AutoRegisterDependency(assemblyName)
method, and inject the class with the [Service] [Repository] [Component] [ServiceFilter]
property into the container.
NAutowired
provides WithAutowired
、GetServiceWithAutowired
extension methods,which can obtain services from containers and automatically resolve their [Autowired]
dependencies. It's particularly convenient when you need to manually obtain services or resolve existing instances.
services.AddSingleton(sp =>
{
var foo = sp.GetServiceWithAutowired<IFooService>();
return foo.Create();
});