Утечка памяти в 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 ничего не говорит об этом, я ожидаю, что он будет обсуждаться повсюду, если это ошибка.
Ответы
Ответ 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 с любым контейнером.
- Запрос приходит и создается новая область для запроса.
- В рамках этой области вы создаете другой объект.
- Используйте эти объекты в другом промежуточном программном обеспечении.
- и когда область действия закончена, она будет удалена.
- любые объекты в пределах этой области, которые не расположены, также удаляются.