我搜索了一个简单的示例,说明如何将DryIoc容器配置为将依赖项简单地注入属性,就像它注入构造函数args一样。
鉴于下面的工作示例..。
货柜登记:
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作为属性注入?
所需的Widget服务:
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现在似乎试图通过使用我没有的无arg构造函数来初始化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中以null结尾。这是更新的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)"
}
}发布于 2016-04-28 02:01:33
第一次会议应改为:
public static void Register(HttpConfiguration config)
{
var c = new Container().WithWebApi(config);
c.Register<IWidgetService, WidgetService>(Reuse.Singleton);
c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton);
// Resolve service first then inject properties into it.
var ws = c.Resolve<IWidgetService>();
c.InjectPropertiesAndFields(ws);
}来自代码的第二次尝试应该是有效的,但可能是另外一种情况。要找到答案,您可以将规则更改为:
PropertiesAndFields.All(withNonPublic: false, withPrimitives: false, withFields: false, ifUnresolved: IfUnresolved.Throw)不过,更好的选择是为精确服务指定确切的属性:
c.Register<IWidgetService, WidgetService>(Reuse.Singleton,
made: PropertiesAndFields.Of.Name("WidgetRepository"));或强类型:
c.Register<IWidgetService, WidgetService>(
Made.Of(() => new WidgetService { WidgetRepository = Arg.Of<IWidgetRepository>() }),
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<A>(Reuse.Singleton);
using (var scope = config.DependencyResolver.BeginScope())
{
var propController = (PropController)scope.GetService(typeof(PropController));
Assert.IsNotNull(propController.A);
}
}
public class PropController : ApiController
{
public A A { get; set; }
}
public class A {}但是将PropertiesAndFields.Auto改为
PropertiesAndFields.All(false, false, false, IfUnresolved.Throw)从更新3中产生了错误。
更新3:
谢谢你上传样品回购,它帮助发现了问题。
DryIoc PropertiesAndFields.Auto规则将注入所有声明的和基类的属性,这将导致基类中定义的某些属性的错误。好的是,Auto只是一个预定义的规则,您可以定义自己的规则来排除基类属性:
private static IEnumerable<PropertyOrFieldServiceInfo> 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());我已经提交了带有补丁的按下。
BTW在未来/下一个DryIoc版本中,我将提供该API以简化基本或仅声明属性选择。
发布于 2016-04-29 06:03:01
好吧,忽略我最后的评论,可能与此无关。查看异常堆栈跟踪,WebAPI似乎回到了使用Activator.CreateInstance作为控制器,这是因为DryIoc无法解决它。但退路掩盖了实际的DryIoc错误。要找到它,请尝试:
container.WithWebApi(throwIfUnresolved: type => type.IsController());https://stackoverflow.com/questions/36900996
复制相似问题