#c# #asp.net-identity #owin
#c# #asp.net-идентификация #owin
Вопрос:
Я использую OWIN Microsoft.AspNet.Идентификация.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 имеет два обратных вызова — create и dispose, но обратный вызов dispose здесь не зарегистрирован. Я также подтвердил свои подозрения с помощью профилировщика памяти.
Я не вижу Owin на codeplex / github (на самом деле я думал, что это с открытым исходным кодом), поэтому я не знаю, где задать свой вопрос: может ли кто-нибудь еще подтвердить, что это утечка памяти? Я не совсем уверен, потому что Google ничего не говорит об этом, я ожидаю, что это должно обсуждаться везде, если это ошибка.
Комментарии:
1. Я взял на себя смелость переслать это команде Microsoft, которая обрабатывает этот код.
2. Было бы неплохо услышать их ответ, если таковой имеется.
3. Последнее, что я слышал, это расследование проблемы. Основная проблема заключается в том, что идентификационный код не является открытым исходным кодом, и довольно сложно создать запрос на извлечение, чтобы исправить это. На данный момент в codeplex есть заполнитель, но … пока ничего. Это может быть намеком на то, что в конечном итоге он будет с открытым исходным кодом.
4. Хотя наблюдение о том, что одноразовый объект для каждого запроса не удаляется, является ошибкой, отсутствие вызова
Dispose
не приводит к утечке памяти или, по крайней мере, не к утечке управляемой памяти.Dispose
гарантирует, что неуправляемые ресурсы будут освобождены. Управляемая память освобождается сборщиком мусора, а не вызовомDispose
. Если управляемая память «протекает», то это потому, что инфраструктура OWIN постоянно хранит ссылки на каждую новуюDbContext
иUserManager
запрещает сборщику мусора освобождать память при завершении запроса.
Ответ №1:
У меня также есть его проблема, ничто из того, что зарегистрировано в CreatePerOwinContext, не удаляется. Я использую версию v2.1.
Вот временное исправление, которое хорошо работает для меня в качестве обходного пути, пока эта библиотека не будет исправлена. По сути, вам нужно вручную зарегистрировать каждый из типов, которые используют register с 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()
метод в определение вашего класса!
Комментарии:
1. Это может показаться странной причиной, но я хочу оставаться в рамках абстракции OWIN и не хочу ссылаться на System.Web…
2. Это приводит к зависимости от системы. Веб, но это определенно работает!
Ответ №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 с любым контейнером.
- Запрос поступает, и создается новая область для запроса
- В этой области вы создаете другой объект.
- Используйте эти объекты в другом промежуточном программном обеспечении
- и когда область действия заканчивается, она удаляется.
- любые объекты в этой области, которые не удаляются, также удаляются.