Внедрение зависимостей фильтра действий в ASP.NET MVC 3 RC2 с StructureMap - PullRequest
10 голосов
/ 17 декабря 2010

Я играл с поддержкой DI в ASP.NET MVC RC2.

Я реализовал сеанс для каждого запроса для NHibernate и мне нужно внедрить ISession в мой фильтр действий "Единица работы".

Если я обращаюсь непосредственно к контейнеру StructureMap (ObjectFactory.GetInstance) или использую DependencyResolver для получения моего экземпляра сеанса, все работает нормально:

    ISession Session {
        get { return DependencyResolver.Current.GetService<ISession>(); }
    }

Однако, если я пытаюсь использовать мой StructureMapПоставщик фильтра (наследует FilterAttributeFilterProvider). У меня проблемы с фиксацией транзакции NHibernate в конце запроса.

Как будто ISession объекты распределяются между запросами.Я вижу это часто, поскольку все мои изображения загружаются через контроллер MVC, поэтому я получаю около 20 сессий NHibernate, созданных при нормальной загрузке страницы.

Я добавил следующее в свой фильтр действий:

    ISession Session {
        get { return DependencyResolver.Current.GetService<ISession>(); }
    }

    public ISession SessionTest { get; set; }

    public override void OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext) {

        bool sessionsMatch = (this.Session == this.SessionTest);

SessionTest внедряется с использованием поставщика фильтра StructureMap.

Я обнаружил, что на странице с 20 изображениями «sessionMatch» был ложным для 2-3 запросов.

Моя конфигурация StructureMap для управления сеансом выглядит следующим образом:

        For<ISessionFactory>().Singleton().Use(new NHibernateSessionFactory().GetSessionFactory());
        For<ISession>().HttpContextScoped().Use(ctx => ctx.GetInstance<ISessionFactory>().OpenSession());

В global.asax я вызываю следующее в конце каждого запроса:

    public Global() {
        EndRequest += (sender, e) => {
            ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
        };
    }

Безопасен ли этот поток конфигурации?Ранее я вставлял зависимости в тот же фильтр, используя пользовательский IActionInvoker.Это работало нормально до MVC 3 RC2, когда я начал испытывать проблему, описанную выше, поэтому я подумал, что вместо этого попробую использовать поставщика фильтров.

Любая помощь будет принята.

Яиспользуя NHibernate 3 RC и последнюю версию StructureMap

Обновление:

Ниже приведены мои реализации DependencyResolver и FilterAttributeFilterProvider:

    public class StructureMapDependencyResolver : IDependencyResolver {
    private readonly IContainer container;

    public StructureMapDependencyResolver(IContainer container) {
        this.container = container;
    }

    public object GetService(Type serviceType) {
        var instance = container.TryGetInstance(serviceType);
        if (instance==null && !serviceType.IsAbstract){
            instance = AddTypeAndTryGetInstance(serviceType);
        }
        return instance;
    }

    private object AddTypeAndTryGetInstance(Type serviceType) {
        container.Configure(c=>c.AddType(serviceType,serviceType));
        return container.TryGetInstance(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType) {
        return container.GetAllInstances(serviceType).Cast<object>();
    }
}
public class StructureMapFilterAttributeFilterProvider : FilterAttributeFilterProvider
{
    private readonly IContainer container;

    public StructureMapFilterAttributeFilterProvider(IContainer container) {
        this.container = container;
    }

    protected override IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
        return BuildUp(base.GetControllerAttributes(controllerContext, actionDescriptor));
    }

    protected override IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
        return BuildUp(base.GetActionAttributes(controllerContext, actionDescriptor));
    }

    private IEnumerable<FilterAttribute> BuildUp(IEnumerable<FilterAttribute> attributes) {
        foreach (var attr in attributes)
            container.BuildUp(attr);
        return attributes;
    }
}

Ответы [ 3 ]

6 голосов
/ 28 мая 2011

Я думал, что вернусь и предоставлю решение.

Как указывалось выше @Thomas, фильтры действий теперь кэшируются в MVC 3. Это означает, что если вы внедрите объект с предполагаемым коротким сроком службы (например, запрос http), он будет кэширован .

Чтобы исправить, вместо введения ISession мы вводим Func<ISession>. Затем каждый раз, когда нам нужен доступ к ISession, мы вызываем функцию. Это гарантирует, что даже если ActionFilter кэшируется, ISession имеет правильную область.

Мне пришлось настроить StructureMap следующим образом, чтобы внедрить «ленивый» экземпляр (к сожалению, он не внедряет ленивый экземпляр автоматически, как это происходит при внедрении Ctor):

            x.SetAllProperties(p => {
                p.OfType<Func<ISession>>();
            });

Мой обновленный ActionFilter ниже:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class UnitOfWorkAttribute : ActionFilterAttribute {

    public Func<ISession> SessionFinder { get; set; }

    public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext) {
        var session = SessionFinder();
        session.BeginTransaction();
    }

    public override void OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext) {         
        var session = SessionFinder();

        var txn = session.Transaction;

        if (txn == null || !txn.IsActive) return;

        if (filterContext.Exception == null || filterContext.ExceptionHandled)
        {
            session.Transaction.Commit();
        }
        else
        {
            session.Transaction.Rollback();
            session.Clear();
        }
    }
}
2 голосов
/ 16 мая 2011

Я не знаю, поможет ли это, но с MVC 3 фильтры действий теперь кэшируются, а не создаются новыми в начале каждого запроса. Так что, если вы вводите зависимости в конструктор, это не будет хорошо работать. Не могли бы вы опубликовать свою реализацию FilterAttributeFilterProvider?

0 голосов
/ 22 декабря 2010

Вы реализовали свой собственный IDependencyResolver, который использует StructureMap? Похоже, что вы этого не должны делать, потому что вы задали для сеанса HttpContext и все же вы видите отдельные сеансы во время одного и того же запроса.

...