首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用于属性注入的DryIOC容器配置

用于属性注入的DryIOC容器配置
EN

Stack Overflow用户
提问于 2016-04-27 21:12:57
回答 2查看 9.6K关注 0票数 5

我搜索了一个简单的示例,说明如何将DryIoc容器配置为将依赖项简单地注入属性,就像它注入构造函数args一样。

鉴于下面的工作示例..。

货柜登记:

代码语言:javascript
复制
    public static void Register(HttpConfiguration config)
    {
        var c = new Container().WithWebApi(config);

        c.Register<IWidgetService, WidgetService>(Reuse.Singleton);
        c.Register<IWidgetRepository, WidgetRepository>(Reuse.Singleton);
    }

小部件服务:

代码语言:javascript
复制
public class WidgetService : IWidgetService
{
    private readonly IWidgetRepository _widgetRepository;

    public WidgetService(IWidgetRepository widgetRepository)
    {
        _widgetRepository = widgetRepository;
    }

    public IList<Widget> GetWidgets()
    {
        return _widgetRepository.GetWidgets().ToList();
    }
}

小部件库:

代码语言:javascript
复制
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服务:

代码语言:javascript
复制
public class WidgetService : IWidgetService
{
    public IWidgetRepository WidgetRepository { get; set; }

    public IList<Widget> GetWidgets()
    {
        return WidgetRepository.GetWidgets().ToList();
    }
}

失败尝试

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

附录1:

代码语言:javascript
复制
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:

代码语言:javascript
复制
    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更改为初始化容器如下:

代码语言:javascript
复制
var c = new Container(Rules.Default.With(propertiesAndFields: PropertiesAndFields.All(withNonPublic: false,
                                                            withPrimitive: false,
                                                            withFields: false,
                                                            ifUnresolved: IfUnresolved.Throw)))
.WithWebApi(config);

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

代码语言:javascript
复制
{
    "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试图对所有注册项使用属性注入。

代码语言:javascript
复制
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。

代码语言:javascript
复制
public class WidgetController : ApiController
{
    public IWidgetService WidgetService { get; set; }

    // GET api/<controller>
    public List<WidgetSummary> Get()
    {
        return WidgetService.GetWidgetSummaries();
    }
}

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

更新2

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

代码语言:javascript
复制
    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)连接为属性注入:

代码语言:javascript
复制
    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(..)时控制器被不同对待。

代码语言:javascript
复制
{
    "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)"
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-04-28 02:01:33

第一次会议应改为:

代码语言:javascript
复制
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);
}

来自代码的第二次尝试应该是有效的,但可能是另外一种情况。要找到答案,您可以将规则更改为:

代码语言:javascript
复制
PropertiesAndFields.All(withNonPublic: false, withPrimitives: false, withFields: false, ifUnresolved: IfUnresolved.Throw)

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

代码语言:javascript
复制
c.Register<IWidgetService, WidgetService>(Reuse.Singleton, 
     made: PropertiesAndFields.Of.Name("WidgetRepository"));

或强类型:

代码语言:javascript
复制
c.Register<IWidgetService, WidgetService>(
    Made.Of(() => new WidgetService { WidgetRepository = Arg.Of<IWidgetRepository>() }),
    Reuse.Singleton);

更新:

DryIoc的缺省值为“最小惊喜”:类型为DI的单个构造函数,且不使用属性注入。但是,您可以选择--在默认值中简化迁移:

代码语言:javascript
复制
IContainer c = new Container(Rules.Default.With(
    FactoryMethod.ConstructorWithResolvableArguments, 
    propertiesAndFields: PropertiesAndFilds.Auto);

可能对你的案子来说已经足够了。

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

代码语言:javascript
复制
    .WithFactorySelector(Rules.SelectLastRegisteredFactory())
    .WithTrackingDisposableTransients()
    .WithAutoConcreteTypeResolution()

更新2:

这个测试对我有效。

代码语言:javascript
复制
[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只是一个预定义的规则,您可以定义自己的规则来排除基类属性:

代码语言:javascript
复制
private static IEnumerable<PropertyOrFieldServiceInfo> DeclaredPublicProperties(Request request)
{
    return (request.ImplementationType ?? request.ServiceType).GetTypeInfo()
        .DeclaredProperties.Where(p => p.IsInjectable())
        .Select(PropertyOrFieldServiceInfo.Of);
}

然后像这样创建容器:

代码语言:javascript
复制
var c = new Container()
    .With(rules => rules.With(propertiesAndFields: DeclaredPublicProperties))
    .WithWebApi(config, throwIfUnresolved: type => type.IsController());

我已经提交了带有补丁的按下

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

票数 5
EN

Stack Overflow用户

发布于 2016-04-29 06:03:01

好吧,忽略我最后的评论,可能与此无关。查看异常堆栈跟踪,WebAPI似乎回到了使用Activator.CreateInstance作为控制器,这是因为DryIoc无法解决它。但退路掩盖了实际的DryIoc错误。要找到它,请尝试:

代码语言:javascript
复制
container.WithWebApi(throwIfUnresolved: type => type.IsController());
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36900996

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档