Подтвердить что ты не робот

Утечка памяти в Owin.AppBuilderExtensions

Я использую OWIN + Microsoft.AspNet.Identity.Owin(v.2.0.0.0) в веб-приложении. Я регистрирую UserManager/DbContext для каждого веб-запроса, как это рекомендуется:

app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

но ни один из них не используется. Я взглянул на отражатель, и это похоже на ошибку в методе расширения:

public static IAppBuilder CreatePerOwinContext<T>(this IAppBuilder app, Func<IdentityFactoryOptions<T>, IOwinContext, T> createCallback) where T: class, IDisposable
{
    if (app == null)
    {
        throw new ArgumentNullException("app");
    }
    if (createCallback == null)
    {
        throw new ArgumentNullException("createCallback");
    }
    object[] args = new object[1];
    IdentityFactoryOptions<T> options = new IdentityFactoryOptions<T> {
        DataProtectionProvider = app.GetDataProtectionProvider()
    };
    IdentityFactoryProvider<T> provider = new IdentityFactoryProvider<T> {
        OnCreate = createCallback
    };
    options.Provider = provider;
    args[0] = options;
    app.Use(typeof(IdentityFactoryMiddleware<T, IdentityFactoryOptions<T>>), args);
    return app;
}

IdentityFactoryProvider имеет два обратных вызова - создайте и удалите, но утилита обратного вызова здесь не зарегистрирована. Я также подтвердил свое подозрение в профайлерах памяти.

Я не вижу Owin на codeplex/github (на самом деле я думал, что это с открытым исходным кодом), поэтому я не знаю, где задать свой вопрос: может ли кто-нибудь еще подтвердить, что это утечка памяти? Я не уверен, потому что Google ничего не говорит об этом, я ожидаю, что он будет обсуждаться повсюду, если это ошибка.

4b9b3361

Ответ 1

У меня также есть его проблема, ничего, что зарегистрировано в CreatePerOwinContext, будет удалено. Я использую v2.1.

Вот временное исправление, которое хорошо работает для меня как работа, пока эта библиотека не будет исправлена. В основном вам необходимо вручную зарегистрировать каждый из типов, которые используют регистр в CreatePerOwnContext в следующем классе, а затем в конце процедуры запуска вы зарегистрируете этот пользовательский класс:

public sealed class OwinContextDisposal : IDisposable
{
    private readonly List<IDisposable> _disposables = new List<IDisposable>(); 

    public OwinContextDisposal(IOwinContext owinContext)
    {
        if (HttpContext.Current == null) return;

        //TODO: Add all owin context disposable types here
        _disposables.Add(owinContext.Get<MyObject1>());
        _disposables.Add(owinContext.Get<MyObject2>());

        HttpContext.Current.DisposeOnPipelineCompleted(this);
    }

    public void Dispose()
    {
        foreach (var disposable in _disposables)
        {
            disposable.Dispose();
        }
    }
}

В конце вашего процесса запуска зарегистрируйте этот класс:

 app.CreatePerOwinContext<OwinContextDisposal>(
      (o, c) => new OwinContextDisposal(c));

Теперь все будет удалено в конце конвейера запроса правильно.

Ответ 2

Утечка памяти в классе AppBuilderExtensions уже исправлена ​​в последней версии библиотеки Microsoft.AspNet.Identity.Owin (2.2.1).

Я проверил код с помощью Reflector, а также поставил точку останова в методы Dispose() объектов, созданных AppBuilderExtensions.CreatePerOwinContext().

Ответ 3

Вы можете поместить логику для удаления экземпляров, созданных с помощью CreatePeOwinContext(), в том же обратном вызове, который вы используете для создания этих целей. То есть:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.CreatePerOwinContext<ClassIWantOneInstancePerContext>(ClassIWantOneInstancePerContext.Create);

        //other code...
    }
}

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

public class ClassIWantOneInstancePerContext
{
     //other code...

    public static ClassIWantOneInstancePerContext Create()
    {
        ClassIWantOneInstancePerContext TheInstance = new ClassIWantOneInstancePerContext();
        HttpContext.Current.DisposeOnPipelineCompleted(TheInstance);

        return TheInstance;
    }
}

Также не забудьте включить метод Dispose() в определение класса!

Ответ 4

Использование: app.CreatePerRequest<AuthorizationContext>();

Метод расширения:

public static IAppBuilder CreatePerRequest<T>(this IAppBuilder builder )where T:IDisposable
        {
            builder.Use(async (context, next) =>
            {
                var resolver = context.Get<IDependencyScope>();

                using (var instance = (T) resolver.GetService(typeof (T)))
                {
                    context.Set<T>(instance);
                    if (next != null)
                    {
                        await next();
                    }
                }

            });

            return builder;
        }

Чтобы использовать инъекцию зависимостей, вам нужно настроить owin: app.UseScopePerOwinRequest(_dependencyResolver); - это должно быть первое промежуточное программное обеспечение.

public static IAppBuilder UseScopePerOwinRequest(this IAppBuilder builder,IDependencyResolver resolver)
        {
            builder.Use(async (context, next) =>
            {
                using (var instance = resolver.BeginScope())
                {
                    context.Set<IDependencyScope>(instance);
                    if (next != null)
                    {
                        await next();
                    }
                }

            });

            return builder;
        }

И для выполнения вышеописанного кода вам необходимо реализовать IDepedencyResolver с любым контейнером.

  • Запрос приходит и создается новая область для запроса.
  • В рамках этой области вы создаете другой объект.
  • Используйте эти объекты в другом промежуточном программном обеспечении.
  • и когда область действия закончена, она будет удалена.
  • любые объекты в пределах этой области, которые не расположены, также удаляются.