kirov-opensource / NAutowired

ASP.NET CORE Field Injection Implement
MIT License
77 stars 16 forks source link
asp-net-core autowired csharp-autowired dependency-injection field-injection nautowired net-core netcore

NAutowired

NuGet Publish Workflow NuGet GitHub issues GitHub repo size in bytes GitHub top language

ASP.NET CORE Field Injection

Idea and positioning

How to use

ASP.NET Core 3.0

ASP.NET Core 6.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:

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";
    }
  }

Use in Filter

  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 Console >= 3.0

Advanced

You can inject a specific type with the [Autowired(Type)] method

  [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 WithAutowiredGetServiceWithAutowired 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();
});

Stargazers over time

Stargazers over time