【问题标题】:Simple Injector - How to inject a property with type Func<T>简单注入器 - 如何注入 Func<T> 类型的属性
【发布时间】:2015-03-23 19:34:47
【问题描述】:

我正在使用一个在类中具有如下属性的代码示例(它使用 StructureMap 进行 DI):

public Func<IUnitOfWork> UnitOfWorkFactory { get; set; }

按照 Simple Injector 教程,我最终得到了以下代码,它应该会执行属性注入:

public class DependencyAttributeSelectionBehaviour : IPropertySelectionBehavior
{
    public bool SelectProperty(Type type, PropertyInfo prop)
    {
        return prop.GetCustomAttributes(typeof(Dependency)).Any();
    }
}

public class Dependency: Attribute
{
       // dummy class
}

Global.asax 内部

 var container = new Container();
 container.Options.PropertySelectionBehavior = new DependencyAttributeSelectionBehaviour();

然后我继续将房产装饰为:

[Dependency]
public Func<IUnitOfWork> UnitOfWorkFactory { get; set; }

但是当UnitOfWorkFactory() 被调用时,我仍然得到一个空异常。

依赖和全局属性在 Global.asax 中初始化如下:

    // Create the container as usual.
    var container = new Container();
    container.Options.PropertySelectionBehavior = new DependencyAttributeSelectionBehaviour();

    // Register your types, for instance using the RegisterWebApiRequest
    // extension from the integration package:
    container.RegisterWebApiRequest<IUnitOfWork, NHibernateUnitOfWork>();
    container.RegisterSingle<ISessionSource, NHibernateSessionSource>();
    container.RegisterSingle<IAppSettings, AppSettings>();

    container.Register(() =>
    {
        var uow = (INHibernateUnitOfWork) (container.GetInstance<IUnitOfWork>());
        return uow.Session;
    });

    container.Register<IUserRepository, UserRepository>();

    // This is an extension method from the integration package.
    container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

    container.Verify();

    GlobalConfiguration.Configuration.DependencyResolver =
        new SimpleInjectorWebApiDependencyResolver(container);

    GlobalConfiguration.Configuration.Filters.Add(new UnitOfWorkAttribute());

这是有道理的,因为属性注入不会神奇地为我的函数属性定义一个主体,但我不知道如何实现这一点。任何想法/提示都表示赞赏。

编辑:

    public class UnitOfWorkAttribute : ActionFilterAttribute
{
    [Dependency]
    private Func<IUnitOfWork> UnitOfWorkFactory { get; set; }

    public override void OnActionExecuting(HttpActionContext filterContext)
    {
        UnitOfWorkFactory().Begin();
    }

    // TODO: Fix this: We should also probably check if exception is handled
    public override void OnActionExecuted(HttpActionExecutedContext filterContext)
    {
        var uow = UnitOfWorkFactory();

        try
        {
            if (filterContext.Exception != null)
            {
                uow.Rollback();
            }
            else
            {
                uow.Commit();
            }
        }
        catch
        {
            uow.Rollback();
            throw;
        }
        finally
        {
            uow.Dispose();
        }
    }
}

【问题讨论】:

  • 我试图重现这个,但我没有成功。如果我使用您的代码,容器会抛出“无法注入名称为 'UnitOfWorkFactory' 的 Func 类型的属性,因为找不到 Func 类型的注册。”当我尝试解析包含该属性的对象时。您能否更新您的问题并展示您的问题的完整工作示例?
  • @Steven 所以你抓住了我:) 所以我们开始吧,然后我提供了更多细节你还需要什么?我所要做的就是用一个实例初始化该属性,但我没有尝试过任何工作,所以我没有想法..
  • 试试这个container.Register&lt;Func&lt;IFoo&gt;&gt;(() =&gt; container.GetInstance&lt;IFoo&gt;(), "UnitOfWorkFactory");
  • @Aron 我试过了:container.Register&lt;Func&lt;IUnitOfWork&gt;&gt;(() =&gt; container.GetInstance&lt;IUnitOfWork&gt;(), "UnitOfWorkFactory"); 我得到了:Cannot implicitly convert type 'TipsBackend.Models.UnitOfWork.IUnitOfWork' to 'System.Func&lt;TipsBackend.Models.UnitOfWork.IUnitOfWork&gt;'
  • @Cemre 我不相信你说你明确键入了通用的 Register 方法。

标签: c# asp.net-web-api2 simple-injector


【解决方案1】:

你的问题开始有道理了。您正在尝试将工厂注入到 Web API 过滤器属性的属性中。属性始终由 CLR 创建;不是通过您的 DI 容器。这就是为什么没有注入依赖项的原因。您可能已经知道 Web API caches filter attributes indefinitely,使它们成为有效的单例,这意味着您不应该向它们注入任何东西,除了单例。这就解释了为什么要注入工厂;工厂可以是单例。

要让它发挥作用,您需要做两件事。首先,您需要将Func&lt;IUnitOfWork&gt; 注册为单例,如下所示:

container.RegisterSingle<Func<IUnitOfWork>>(container.GetInstance<IUnitOfWork>);

您需要做的第二件事是创建一个自定义IFilterProvider,它允许将属性注入到属性中。

事实上,Simple Injector 的 Web API 集成库包含一个扩展方法,该方法将自定义过滤器提供程序注册到执行此操作的 Web API 管道中。

虽然这种扩展方法听起来像是答案;不是。这不是答案,因为此扩展方法将在下一个小版本 (2.8) 中标记为 [Obsolete],并将在下一个主要版本 (3.0) 中从库中删除。这样做的原因是很容易意外地将依赖项注入到不是单例的属性中,这可能会导致各种讨厌的并发问题。但由于容器无法创建属性,Simple Injector 是盲目的,无法使用 Diagnostic Services 向您报告此错误。

因此,我们决定在未来的版本中移除此功能。 Web API integration guide wiki page 中解释了替代方案。基本上,文档说您应该退回到服务定位器模式(通过调用DependencyResolver.Current.GetService)或使您的属性被动,如here 所述。

【讨论】:

  • 这是因为示例是这样的,但我愿意接受任何建议,但不必如此。这会返回一个错误说cannot implicitly convert type 'TipsBackend.Models.UnitOfWork.IUnitOfWork' to 'System.Func&lt;TipsBackend.Models.UnitOfWork.IUnitOfWork&gt;'
  • 相反我尝试了这个container.RegisterWebApiRequest&lt;IUnitOfWork&gt;(container.GetInstance&lt;NHibernateUnitOfWork&gt;);,但我仍然得到空错误
  • @Cemre:请更新您的问题并尽可能具体。谁给你错误?编译器?编译器的确切错误信息是什么?你在运行时遇到异常吗?确切的异常消息、异常类型和堆栈跟踪是什么?还请显示包含此Func&lt;IUnitOfWork&gt; 属性的类。
  • 我更新了显示课程的问题。因此,当我运行程序并尝试进行 api/values 之类的 api 调用时,程序会尝试启动一个工作单元,但由于该类的属性 (UnitOfWorkFactory ) 为空而失败。错误是null reference exception
  • @Cemre:啊……好吧,我看到了你的更新。您正在尝试注入属性的属性!这完全改变了故事。让我更新问题...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-07
  • 2018-08-23
  • 1970-01-01
  • 2018-12-30
  • 2017-05-05
  • 1970-01-01
相关资源
最近更新 更多