我已经广泛搜索了一个如何配置 DryIoc 容器以简单地将依赖项作为属性注入的简单示例,就像它注入构造函数参数一样。
鉴于以下工作示例...
容器注册:
public static void Register(HttpConfiguration config)
{
var c = new Container().WithWebApi(config);
c.Register<IWidgetService, WidgetService>(Reuse.Singleton);
c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton);
}
小部件服务:
public class WidgetService : IWidgetService
{
private readonly IWidgetRepository _widgetRepository;
public WidgetService(IWidgetRepository widgetRepository)
{
_widgetRepository = widgetRepository;
}
public IList<Widget> GetWidgets()
{
return _widgetRepository.GetWidgets().ToList();
}
}
小部件存储库:
public class WidgetRepository : IWidgetRepository
{
private readonly IList<Widget> _widgets;
public WidgetRepository()
{
_widgets = new List<Widget>
{
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<Widget> GetWidgets()
{
return _widgets.AsEnumerable();
}
}
配置需要如何更改以支持看起来像这样的 WidgetService,其中 DryIoc 将 WidgetRepository 作为属性注入?
所需的小部件服务:
public class WidgetService : IWidgetService
{
public IWidgetRepository WidgetRepository { get; set; }
public IList<Widget> GetWidgets()
{
return WidgetRepository.GetWidgets().ToList();
}
}
失败的尝试
我已经尝试过这些配置更改,但它们似乎对在 WidgetService 上启用属性注入没有影响。
尝试1:
public static void Register(HttpConfiguration config)
{
var c = new Container().WithWebApi(config);
// Seems logical - no luck
c.InjectPropertiesAndFields(PropertiesAndFields.Auto);
c.Register<IWidgetService, WidgetService>(Reuse.Singleton);
c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton);
}
尝试 2:
public static void Register(HttpConfiguration config)
{
var c = new Container(Rules.Default.With(propertiesAndFields: PropertiesAndFields.Auto))
.WithWebApi(config);
c.Register<IWidgetService, WidgetService>(Reuse.Singleton);
c.Register<IWidgetRepository, WidgetRepository>(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.<SendAsync>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/<controller>
public List<WidgetSummary> Get()
{
return _widgetService.GetWidgetSummaries();
}
}
我试图使用属性注入来初始化 WidgetService(如上所示),但 WidgetController 使用构造函数注入。也许我不能两者都做,但我认为PropertiesAndFields.Auto
规则会允许两者。我也更改了 WidgetController 以设置属性注入。然后我没有从 DryIoc 中得到任何异常,但 WidgetService 在 WidgetController 中最终为空。这是更新后的 WidgetController。
public class WidgetController : ApiController
{
public IWidgetService WidgetService { get; set; }
// GET api/<controller>
public List<WidgetSummary> 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<IWidgetService, WidgetService>(Reuse.Singleton, autoInjectProps);
c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton, autoInjectProps);
}
我很想最终找出适用于控制器和其他服务的黄金设置,但它们的行为似乎不同。但是,目前这是一个足够可行的解决方案。
更新 3
根据@dadhi 的建议将容器配置更新为以下内容,再次尝试将所有内容(包括 WidgetController)连接为属性注入:
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<IWidgetService, WidgetService>(Reuse.Singleton);
c.Register<IWidgetRepository, WidgetRepository>(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.<SendAsync>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)"
}
}