Aggregate Services

https://stackoverflow.com/questions/7199097/constructor-injection-into-a-base-class-using-autofac

Calling the base class constructor explicitly is the only way to do this using constructor injection in C#. It looks like you should remove the parameterless constructors from BaseController and PublicController as they should never actually be called when a logger is available.

The problem of injecting dependencies into a base controller is a common one using ASP.NET MVC and IoC. There are several options/schools of thought.

1.) Use aggregate services. To keep derived class constructors simple, create a single service that exposes or delegates to all the different services needed by the base controller (e.g. IBaseControllerDependencies or similar.) Then pass this one service to the BaseController just as you are doing with ILogger here.

There are various pros/cons depending on your application and the number of base classes you're using. Google for 'Autofac aggregate services' to see more on this.

2.) Use property injection. Make the ILogger property on your base class public, and configure the container using:

builder.RegisterControllers().PropertiesAutowired();

Property injection isn't really a preferred technique in Autofac. The constructor's role is to accept dependencies, while writeable properties are often seen as a code smell, so Autofac doesn't really optimise for this case. One of the drawbacks is that writeable properties that shouldn't be injected often are mistakenly, with odd consequences.

3.) Refactor base controller functionality into various action filters. Autofac can inject action filters into the MVC action invocation pipeline. Thus the filters can take the dependencies that were on the base class, and the same concerns might be applied in a cross-cutting way. More info on this out on the web, ExtensibleActionInvoker and .InjectActionInvoker() point to the info you'd need. Not always possible with all concerns.

4, also the answer to your second question.) Resolve base controller dependencies using service location from DependencyResolver.Current.

var logger = DependencyResolver.Current.GetService<ILogger>();

The reason this isn't encouraged is that it makes the resulting application harder to understand because it is no longer possible to see what services a component depends upon by looking in one place (the constructor.) To determine what must be configured in the container before a particular component can be used, one has to look at the entire codebase of the component to find the GetService() calls. A noticeable impediment when unit testing.

Hope this helps, bit of a brain dump I know :) Others can probably add some more ideas to these.

Refactoring dependencies with Autofac Aggregate Services  如何使用

Aggregate Services   autofac官方文档

Introduction

An aggregate service is useful when you need to treat a set of dependencies as one dependency. When a class depends on several constructor-injected services, or have several property-injected services, moving those services into a separate class yields a simpler API.

An example is super- and subclasses where the superclass have one or more constructor-injected dependencies. The subclasses must usually inherit these dependencies, even though they might only be useful to the superclass. With an aggregate service, the superclass constructor parameters can be collapsed into one parameter, reducing the repetitiveness in subclasses. Another important side effect is that subclasses are now insulated against changes in the superclass dependencies, introducing a new dependency in the superclass means only changing the aggregate service definition.

The pattern and this example are both further elaborated here.

Aggregate services can be implemented by hand, e.g. by building a class with constructor-injected dependencies and exposing those as properties. Writing and maintaining aggregate service classes and accompanying tests can quickly get tedious though. The AggregateService extension to Autofac lets you generate aggregate services directly from interface definitions without having to write any implementation.

Required References

You can add aggregate service support to your project using the Autofac.Extras.AggregateService NuGet package or by manually adding references to these assemblies:

Getting Started

Lets say we have a class with a number of constructor-injected dependencies that we store privately for later use:

public class SomeController
{
  private readonly IFirstService _firstService;
  private readonly ISecondService _secondService;
  private readonly IThirdService _thirdService;
  private readonly IFourthService _fourthService;

  public SomeController(
    IFirstService firstService,
    ISecondService secondService,
    IThirdService thirdService,
    IFourthService fourthService)
  {
    _firstService = firstService;
    _secondService = secondService;
    _thirdService = thirdService;
    _fourthService = fourthService;
  }
}

To aggregate the dependencies we move those into a separate interface definition and take a dependency on that interface instead.

public interface IMyAggregateService
{
  IFirstService FirstService { get; }
  ISecondService SecondService { get; }
  IThirdService ThirdService { get; }
  IFourthService FourthService { get; }
}

public class SomeController
{
  private readonly IMyAggregateService _aggregateService;

  public SomeController(IMyAggregateService aggregateService)
  {
    _aggregateService = aggregateService;
  }
}

Finally, we register the aggregate service interface.

using Autofac;
using Autofac.Extras.AggregateService;
//...

var builder = new ContainerBuilder();
builder.RegisterAggregateService<IMyAggregateService>();
builder.Register(/*...*/).As<IFirstService>();
builder.Register(/*...*/).As<ISecondService>();
builder.Register(/*...*/).As<IThirdService>();
builder.Register(/*...*/).As<IFourthService>();
builder.RegisterType<SomeController>();
var container = builder.Build();

The interface for the aggregate service will automatically have an implementation generated for you and the dependencies will be filled in as expected.

controller的顺序不对的话,

{
"Message": "An error has occurred.",
"ExceptionMessage": "An error occurred when trying to create a controller of type 'CodeStocksController'. Make sure that the controller has a parameterless public constructor.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": " at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__15.MoveNext()",
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Type 'LISA.WebApi.Chile.Controllers.CodeStocksController' does not have a default constructor",
"ExceptionType": "System.ArgumentException",
"StackTrace": " at System.Linq.Expressions.Expression.New(Type type)\r\n at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"
}
}

扩展阅读

Dependency Injection and Class Inheritance    官方文档提到的一篇文章

Refactoring to Aggregate Services    最原始的讨论

猜你喜欢

转载自www.cnblogs.com/chucklu/p/10333182.html
今日推荐