Ninject InRequestScope возвращается к InThreadScope

В моем проекте MVC3 я настроил свое ядро ​​на Ninject контекст Entityframework на основе InRequestScope, это работает отлично, но у меня есть фоновый бегун, который выполняет некоторые управления рабочим процессом.

Он запускает новый поток каждые 5 минут, а я Ninject меняет зависимости в этом потоке. Если я изменю область действия на InThreadScipe, то будет запущен метод Dispose, но если я верну его обратно в InRequestScope, метод Dispose не будет запущен.

Есть ли способ отступить к InThreadScope, если InRequestScope недоступен?

Обновление: Только что получил ответ на этот вопрос и почему бы не обновить его дополнительной информацией. Я думаю, что Ninjects способ обработки жизненного времени немного устарел. Другие IoC имеют дочерние контейнеры. Транзитные зарегистрированные объекты живут в течение всего дочернего контейнера и размещаются, когда находятся дочерние контейнеры. Это гораздо более простой способ объединения, например, Web API с пользовательским рабочим, как описано выше.

Ответ 1

Существует метод InScope, с помощью которого вы можете указать настраиваемое правило видимости.

Реализация InRequestScope в настоящее время претерпевает изменения с 2.2-2.4.

Итак, перейдите к исходной версии вашей версии, посмотрите на тег InRequestScope и InThreadScope и создайте амальгаму (которую вы затем можете вставить как метод расширения вместе с другими методами InXXXScope.

Он будет выглядеть (прежде чем вы извлечете его в метод расширения), например:

Bind<X>.To<T>().InScope( ctx => ScopeContext.Request(ctx) ?? ScopeContext.Thread(ctx))

Другим способом является создание двух Bind ings с соответствующим ограничением, например WhenInjectedInto<T>, чтобы настроить область охвата на основе контекстной привязки.

Ответ 2

Благодаря Рубену я нашел решение, однако там прокрался небольшой клочок там в его псевдокоде и с тех пор, как в понедельник и я устал, я не видел его сразу: D

StandardScopeCallbacks.Request

Является делегатом и

StandardScopeCallbacks.Request ?? StandardScopeCallbacks.Thread 

всегда будет возвращать левую сторону, так как делегат никогда не будет пустым.

Есть два способа сделать это правильно,

1 ExtensionMethod

public static IBindingWhenInNamedWithOrOnSyntax<T> InRequestFallbackScope<T>(this IBindingWhenInNamedWithOrOnSyntax<T> binding)
{
    Func<IContext, object> fallbackCallback = ctx => StandardScopeCallbacks.Request(ctx) ?? StandardScopeCallbacks.Thread(ctx);
    binding.Binding.ScopeCallback = fallbackCallback;
    return binding;
}

2 Используя метод InScope

.InScope(ctx => StandardScopeCallbacks.Request(ctx) ?? StandardScopeCallbacks.Thread(ctx))

Ответ 3

Это то, что я написал, чтобы иметь область запроса в первую очередь, и поток как резерв.

public static class NinjectBindingExtensions
{
    private static bool IsRequestPresent()
    {
        try
        {
            return HttpContext.Current != null;
        }
        catch
        {
            return false;
        }
    }

    private static object GetScope(IContext ctx)
    {
        // note: this is a copy of the private method of Ninject.Web.Common.RequestScopeExtensionMethod#GetScope
        return ctx.Kernel.Components.GetAll<INinjectHttpApplicationPlugin>().Select(c => c.RequestScope).FirstOrDefault(s => s != null);
    }

    public static IBindingWhenInNamedWithOrOnSyntax<T> InRequestFallbackScope<T>(this IBindingWhenInNamedWithOrOnSyntax<T> binding)
    {
        Func<IContext, object> fallbackCallback = ctx => IsRequestPresent() ? GetScope(ctx) : StandardScopeCallbacks.Thread(ctx);
        binding.BindingConfiguration.ScopeCallback = fallbackCallback;
        return binding;
    }
}

Если вы находитесь в сценарии Thread, вы вызываете _kernel.Components.Get<ICache>().Clear(Thread.CurrentThread); для запуска (возможно, существующего) Dispose() для объектов, загруженных в эту область (- > Thread.CurrentThread).