Использование Unity для внедрения зависимостей в пользовательский ActionFilter - PullRequest
8 голосов
/ 20 ноября 2011

На данный момент у меня есть пользовательская ControllerFactory, в которую я вставляю свой контейнер Unity:

в global.asax Application_Start ():

var container = InitContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));

var factory = new UnityControllerFactory(container);
ControllerBuilder.Current.SetControllerFactory(factory);

В фабрике контроллеров я установил свойконтроллеры для использования собственного ActionInvoker, например, так:

protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
    var controller = base.GetControllerInstance(requestContext, controllerType) as Controller;

    if (controller != null)
        controller.ActionInvoker = new UnityActionInvoker(_container);

    return controller;
}

Наконец, в своем пользовательском ActionInvoker я пытаюсь создать действия, вызываемые с помощью контейнера ActionInvokers:

protected override ActionExecutedContext InvokeActionMethodWithFilters(
        ControllerContext controllerContext,
        IList<IActionFilter> filters,
        ActionDescriptor actionDescriptor,
        IDictionary<string, object> parameters)
{
    var builtUpFilters = new List<IActionFilter>();

    foreach (IActionFilter actionFilter in filters)
    {
        builtUpFilters.Add(_container.BuildUp<IActionFilter>(actionFilter));
    }

    return base.InvokeActionMethodWithFilters(controllerContext, builtUpFilters, actionDescriptor, parameters);
}

Вот примеродин из создаваемых ActionFilters:

public class PopulatRolesAttribute : ActionFilterAttribute, IActionFilter
{
    private const string RolesKey = "roles";

    [Dependency]
    public Func<IMetadataService> Service { get; set; }

    public PopulatRolesAttribute()
    {
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.Controller.ViewData[RolesKey] == null)
        {
            filterContext.Controller.ViewData[RolesKey] = Service().GetRoles();
        }
    }
}

Проблема в том, что открытое свойство в моем пользовательском атрибуте ActionFilterAttribute никогда не внедряется с чем-либо, оно остается нулевым при выполнении!Я не могу понять, почему мой фильтр неправильно собирается контейнером.Внедряемый тип регистрируется должным образом, например:

container.RegisterInstance(new ChannelFactory<IMetadataService>(
    new BasicHttpBinding(),
    new EndpointAddress("http://example.com/ABSApplication/MetadataService.svc")));

container.RegisterInstance<Func<IMetadataService>>(
    () => container.Resolve<ChannelFactory<IMetadataService>>().CreateChannel());

И также внедряется в другом месте приложения (хотя и не через .Buildup).Это почти тот же процесс, за которым следует сообщение в блоге .Какой кусок головоломки мне не хватает?

1 Ответ

8 голосов
/ 20 ноября 2011

Я бы сделал это немного по-другому.Я хотел бы:

  1. установить пакет nuget unity.mvc3

  2. вызвать bootstrapped.initialise (), как указано в документе txt, добавляемом пакетомк проекту

  3. определите ваше отображение IMetadataService в инициализации вашего типа бетона

  4. добавьте IMetadataService в ваш конструктор

Разница между вашей реализацией и статьей, на которую вы ссылаетесь, заключается в том, что вы используете Func, и я не уверен, добавит ли это другую проблему в этот микс.Я должен представить, что это работает, так как описанный выше метод (без Func) отлично работает для меня.

Редактировать: Код Брэда Уилсона отлично работает для меня здесь: http://bradwilson.typepad.com/blog/2010/07/service-location-pt4-filters.html

Применимые части (пожалуйста,см. ссылку выше)

Global.asax.cs


protected void Application_Start() {
    // ...

    var oldProvider = FilterProviders.Providers.Single(
        f => f is FilterAttributeFilterProvider
    );
    FilterProviders.Providers.Remove(oldProvider);

    var container = new UnityContainer();
    var provider = new UnityFilterAttributeFilterProvider(container);
    FilterProviders.Providers.Add(provider);

    // ...
}

Сам фильтр:


using System;
using System.Web.Mvc;
using Microsoft.Practices.Unity;

public class InjectedFilterAttribute : ActionFilterAttribute {

    [Dependency]
    public IMathService MathService { get; set; }

    public override void OnResultExecuted(ResultExecutedContext filterContext) {
        filterContext.HttpContext.Response.Write(
            String.Format("<p>The filter says 2 + 3 is {0}.</p>",
                          MathService.Add(2, 3))
        );
    }
}

и UnityFilterAttributeFilterProvider.cs


using System.Collections.Generic;
using System.Web.Mvc;
using Microsoft.Practices.Unity;

public class UnityFilterAttributeFilterProvider : FilterAttributeFilterProvider {
    private IUnityContainer _container;

    public UnityFilterAttributeFilterProvider(IUnityContainer container) {
        _container = container;
    }

    protected override IEnumerable GetControllerAttributes(
                ControllerContext controllerContext,
                ActionDescriptor actionDescriptor) {

        var attributes = base.GetControllerAttributes(controllerContext,
                                                      actionDescriptor);
        foreach (var attribute in attributes) {
            _container.BuildUp(attribute.GetType(), attribute);
        }

        return attributes;
    }

    protected override IEnumerable GetActionAttributes(
                ControllerContext controllerContext,
                ActionDescriptor actionDescriptor) {

        var attributes = base.GetActionAttributes(controllerContext,
                                                  actionDescriptor);
        foreach (var attribute in attributes) {
            _container.BuildUp(attribute.GetType(), attribute);
        }

        return attributes;
    }
}

...