NAutowired
ASP.NET CORE Field Injection
Idea and positioning
- 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?
How to use
- 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.
ASP.NET Core 3.0
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
.
IControllerActivator
implementation with NAutowiredControllerActivator
in Startup.cs
Replace the default 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>();
}
[Route("api/[controller]")]
[ApiController]
public class FooController : ControllerBase {
//Use Autowired injection.
[Autowired]
private readonly FooService fooService;
[HttpGet]
public ActionResult<string> Get() {
return fooService == null ? "failure" : "success";
}
}
Filter
Use in 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;
}
}
Get Configuration
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
}
}
NET Core 3.0
Console
Srartup.cs
file and inherit from NAutowired.Core.Startup
Create a new 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();
}
}
Program.cs
In 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>();
*/
}
}
Unit Test
Advanced
[Autowired(Type)]
method
You can inject a specific type with the [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" });
}
}
[Service] [Repository] [Component] [ServiceFilter]
attribute tag class, these classes will be added to the container when AutoRegisterDependency
is executed
Use the //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 AddAutoDependencyInjection(assemblyName)
method, and inject the class with the [Service] [Repository] [Component] [ServiceFilter]
property into the container.