DryIOC容器配置用于属性注入

我已经搜索了一个简单的例子来说明如何配置DryIoc容器以简单地将依赖关系注入属性,就像它注入构造函数args一样。

鉴于以下工作示例……

集装箱登记:

public static void Register(HttpConfiguration config) { var c = new Container().WithWebApi(config); c.Register(Reuse.Singleton); c.Register(Reuse.Singleton); } 

小部件服务:

 public class WidgetService : IWidgetService { private readonly IWidgetRepository _widgetRepository; public WidgetService(IWidgetRepository widgetRepository) { _widgetRepository = widgetRepository; } public IList GetWidgets() { return _widgetRepository.GetWidgets().ToList(); } } 

小部件存储库:

 public class WidgetRepository : IWidgetRepository { private readonly IList _widgets; public WidgetRepository() { _widgets = new List { new Widget {Name = "Widget 1", Cost = new decimal(1.99), Description = "The first widget"}, new Widget {Name = "Widget 2", Cost = new decimal(2.99), Description = "The second widget"} }; } public IEnumerable GetWidgets() { return _widgets.AsEnumerable(); } } 

如何配置需要更改以支持看起来像这样的WidgetService,其中DryIoc将WidgetRepository注入属性?

所需的小部件服务:

 public class WidgetService : IWidgetService { public IWidgetRepository WidgetRepository { get; set; } public IList GetWidgets() { return WidgetRepository.GetWidgets().ToList(); } } 

失败的尝试

我尝试过这些配置更改,但它们似乎对在WidgetService上启用属性注入没有任何影响。

ATTEMP 1:

 public static void Register(HttpConfiguration config) { var c = new Container().WithWebApi(config); // Seems logical - no luck c.InjectPropertiesAndFields(PropertiesAndFields.Auto); c.Register(Reuse.Singleton); c.Register(Reuse.Singleton); } 

ATTEMP 2:

  public static void Register(HttpConfiguration config) { var c = new Container(Rules.Default.With(propertiesAndFields: PropertiesAndFields.Auto)) .WithWebApi(config); c.Register(Reuse.Singleton); c.Register(Reuse.Singleton); } 

我也尝试了上面的PropertiesAndFields.All ,也没有运气。

注意:我知道属性注入不是推荐的方法,并且构造函数注入是首选的原因有很多。 但是,我想知道如何正确地做到这两点。

更新

按照@ dadhi的建议,我改变了尝试#2来初始化容器,如:

 var c = new Container(Rules.Default.With(propertiesAndFields: PropertiesAndFields.All(withNonPublic: false, withPrimitive: false, withFields: false, ifUnresolved: IfUnresolved.Throw))) .WithWebApi(config); 

但后来我收到了这个错误:

 { "Message" : "An error has occurred.", "ExceptionMessage" : "An error occurred when trying to create a controller of type 'WidgetController'. 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.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()", "InnerException" : { "Message" : "An error has occurred.", "ExceptionMessage" : "Type 'IOCContainerTest.DryIOC.Controllers.WidgetController' 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)" } } 

DryIoc现在似乎试图通过使用我没有的无参数构造函数来初始化我的WidgetController。 我假设,因为规则更改为PropertiesAndFields.All(...) ,DryIoc正在尝试对所有已注册的项目使用属性注入。

 public class WidgetController : ApiController { private readonly IWidgetService _widgetService; public WidgetController(IWidgetService widgetService) { _widgetService = widgetService; } // GET api/ public List Get() { return _widgetService.GetWidgetSummaries(); } } 

我试图使用属性注入初始化WidgetService(如上所示),但是WidgetController使用构造函数注入。 也许我不能同时做到这两点,但我认为PropertiesAndFields.Auto规则允许两者兼而有之。 我也改变了WidgetController来设置属性注入。 然后我从DryIoc中得到没有exception,但是WidgetService在WidgetController中结束为null。 这是更新的WidgetController。

 public class WidgetController : ApiController { public IWidgetService WidgetService { get; set; } // GET api/ public List Get() { return WidgetService.GetWidgetSummaries(); } } 

自动属性注入似乎仍然难以捉摸。

更新2

经过多次试验和错误(以及来自@dadhi的建议),我决定在我的WidgetController中使用构造函数注入,并在注册其他服务时指定属性注入。 这允许将利用属性注入的代码迁移到构造函数注入,但控制器除外。 这是我更新的容器注册:

  public static void Register(HttpConfiguration config) { var c = new Container(Rules.Default).WithWebApi(config); var autoInjectProps = Made.Of(propertiesAndFields: PropertiesAndFields.Auto); c.Register(Reuse.Singleton, autoInjectProps); c.Register(Reuse.Singleton, autoInjectProps); } 

我很想最终找出适用于控制器和其他服务的黄金环境,但它们似乎采取不同的行动。 但是,现在这是一个可行的解决方案。

更新3

在另一次尝试将所有内容(包括WidgetController)连接为属性注入时,将@addhi建议的容器配置更新为以下内容:

  public static void Register(HttpConfiguration config) { var c = new Container(Rules.Default.With(propertiesAndFields: PropertiesAndFields.All(withNonPublic: false, withPrimitive: false, withFields: false, ifUnresolved: IfUnresolved.Throw))) .WithWebApi(config, throwIfUnresolved: type => type.IsController()); c.Register(Reuse.Singleton); c.Register(Reuse.Singleton); } 

这似乎至少会产生一个例外情况,这可能会解释为什么当我设置容器以使用PropertiesAndFields.All(..)时控制器被处理不同:

 { "Message" : "An error has occurred.", "ExceptionMessage" : "An error occurred when trying to create a controller of type 'WidgetController'. 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.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()", "InnerException" : { "Message" : "An error has occurred.", "ExceptionMessage" : "Unable to resolve HttpConfiguration as property \"Configuration\"\r\n in IOCContainerTest.DryIOC.Controllers.WidgetController.\r\nWhere no service registrations found\r\n and number of Rules.FallbackContainers: 0\r\n and number of Rules.UnknownServiceResolvers: 0", "ExceptionType" : "DryIoc.ContainerException", "StackTrace" : " at DryIoc.Throw.It(Int32 error, Object arg0, Object arg1, Object arg2, Object arg3)\r\n at DryIoc.Container.ThrowUnableToResolve(Request request)\r\n at DryIoc.Container.DryIoc.IContainer.ResolveFactory(Request request)\r\n at DryIoc.ReflectionFactory.InitPropertiesAndFields(NewExpression newServiceExpr, Request request)\r\n at DryIoc.ReflectionFactory.CreateExpressionOrDefault(Request request)\r\n at DryIoc.Factory.GetExpressionOrDefault(Request request)\r\n at DryIoc.Factory.GetDelegateOrDefault(Request request)\r\n at DryIoc.Container.ResolveAndCacheDefaultDelegate(Type serviceType, Boolean ifUnresolvedReturnDefault, IScope scope)\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)" } } 

第一个应该改为:

 public static void Register(HttpConfiguration config) { var c = new Container().WithWebApi(config); c.Register(Reuse.Singleton); c.Register(Reuse.Singleton); // Resolve service first then inject properties into it. var ws = c.Resolve(); c.InjectPropertiesAndFields(ws); } 

单独使用代码的第二次尝试应该可行,但它可能是其他的。 要了解您可以将规则更改为:

 PropertiesAndFields.All(withNonPublic: false, withPrimitives: false, withFields: false, ifUnresolved: IfUnresolved.Throw) 

但更好的选择是为精确服务指定确切的属性:

 c.Register(Reuse.Singleton, made: PropertiesAndFields.Of.Name("WidgetRepository")); 

或强类型:

 c.Register( Made.Of(() => new WidgetService { WidgetRepository = Arg.Of() }), Reuse.Singleton); 

更新:

DryIoc设计的默认值为最小惊喜:类型为DI,没有属性注入的单一构造函数。 但您可以选择使用默认值来简化迁移:

 IContainer c = new Container(Rules.Default.With( FactoryMethod.ConstructorWithResolvableArguments, propertiesAndFields: PropertiesAndFilds.Auto); 

对你的情况来说可能已经足够了。

如果没有,您可以添加规则:

  .WithFactorySelector(Rules.SelectLastRegisteredFactory()) .WithTrackingDisposableTransients() .WithAutoConcreteTypeResolution() 

更新2:

这个测试对我有用。

 [Test] public void Controller_with_property_injection() { var config = new HttpConfiguration(); var c = new Container() .With(rules => rules.With(propertiesAndFields: PropertiesAndFields.Auto)) .WithWebApi(config, throwIfUnresolved: type => type.IsController()); c.Register(Reuse.Singleton); using (var scope = config.DependencyResolver.BeginScope()) { var propController = (PropController)scope.GetService(typeof(PropController)); Assert.IsNotNull(propController.A); } } public class PropController : ApiController { public AA { get; set; } } public class A {} 

但是将PropertiesAndFields.Auto更改为
PropertiesAndFields.All(false, false, false, IfUnresolved.Throw)从您的更新3中产生错误。

更新3:

感谢您上传示例回购 ,这有助于找到问题所在。

DryIoc PropertiesAndFields.Auto规则将注入所有声明的和基类属性,这会导致基本ApiController类中定义的某些属性的错误。 好处是Auto只是一个预定义规则,您可以定义自己的规则来排除基类属性:

 private static IEnumerable DeclaredPublicProperties(Request request) { return (request.ImplementationType ?? request.ServiceType).GetTypeInfo() .DeclaredProperties.Where(p => p.IsInjectable()) .Select(PropertyOrFieldServiceInfo.Of); } 

然后像这样创建容器:

 var c = new Container() .With(rules => rules.With(propertiesAndFields: DeclaredPublicProperties)) .WithWebApi(config, throwIfUnresolved: type => type.IsController()); 

我已经提交了PR修复程序。

BTW在未来/下一个DryIoc版本中,我将提供API以简化基础或仅声明属性选择。

好的,忽略我的上一条评论,可能不相关。 查看exception堆栈跟踪,似乎WebAPI回退到使用Activator.CreateInstance作为控制器,这是因为DryIoc无法解析它。 但是后备掩盖了实际的DryIoc错误。 要找到它,请尝试:

 container.WithWebApi(throwIfUnresolved: type => type.IsController());