C# Owin.AppBuilderExtensions中的内存泄漏

C# Owin.AppBuilderExtensions中的内存泄漏,c#,asp.net-identity,owin,C#,Asp.net Identity,Owin,我在Web应用程序中使用OWIN+Microsoft.AspNet.Identity.OWIN(v.2.0.0.0)。我按照广泛推荐的方式,为每个web请求注册UserManager/DbContext: app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); app.

我在Web应用程序中使用OWIN+Microsoft.AspNet.Identity.OWIN(v.2.0.0.0)。我按照广泛推荐的方式,为每个web请求注册UserManager/DbContext:

app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext(ApplicationUserManager.Create);
但两人都不愿意。我瞥了一眼反射器,它看起来像是扩展方法中的一个bug:

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;
}
公共静态IAppBuilder CreatePerOwinContext(此IAppBuilder应用程序,Func createCallback),其中T:class,IDisposable
{
如果(app==null)
{
抛出新的ArgumentNullException(“应用”);
}
if(createCallback==null)
{
抛出新ArgumentNullException(“createCallback”);
}
object[]args=新对象[1];
IdentityFactoryOptions=newidentityfactoryoptions{
DataProtectionProvider=app.GetDataProtectionProvider()
};
IdentityFactoryProvider=新的IdentityFactoryProvider{
OnCreate=createCallback
};
选项。提供者=提供者;
args[0]=选项;
app.Use(typeof(IdentityFactoryMiddleware),args);
返回应用程序;
}
IdentityFactoryProvider有两个回调-create和dispose,但dispose回调未在此处注册。我还用内存分析器证实了我的怀疑

我在codeplex/github上没有看到Owin(事实上我认为它是开源的),所以我不知道该在哪里问我的问题:其他人能确认这是内存泄漏吗?
我不太确定,因为谷歌对它只字不提,我希望它应该到处讨论,如果这是一个bug。

我也有他的问题,在CreatePerOwinContext注册的任何东西都不会得到处理。我使用的是v2.1

这里有一个临时修复,在这个lib被修复之前,它对我来说是一个很好的解决方案。基本上,您必须在以下类中手动注册使用register with 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();
        }
    }
}
公共密封类OwinContextDisposal:IDisposable
{
私有只读列表_disposables=新列表();
公共owinContext处理(IOwinContext owinContext)
{
if(HttpContext.Current==null)返回;
//TODO:在此处添加所有owin上下文类型
_Add(owinContext.Get());
_Add(owinContext.Get());
HttpContext.Current.DisposeOnPipelineCompleted(此);
}
公共空间处置()
{
foreach(var一次性使用的可处置物品)
{
一次性的,一次性的;
}
}
}
在启动过程结束时,注册此类:

 app.CreatePerOwinContext<OwinContextDisposal>(
      (o, c) => new OwinContextDisposal(c));
app.CreatePerOwinContext(
(o,c)=>新的处置(c));

现在,所有内容都将在请求管道的末尾正确地处理。

您可以将处理使用
createPeowContext()
创建的实例的逻辑放在创建该实例时使用的回调中。 即:


另外,不要忘记在类定义中包含
Dispose()
方法

用法:
app.CreatePerRequest()

扩展方法:

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;
        }
要使上述代码正常工作,您必须使用任何容器实现IDepedencyResolver

  • 请求进入并创建新的请求范围
  • 在该范围内创建其他对象
  • 在其他中间件中使用这些对象
  • 当范围结束时,它会被处理掉
  • 该范围内未处理的任何对象也会被处理掉

最新版本的
Microsoft.AspNet.Identity.Owin
库()中已修复了
AppBuilderExtensions
类中的内存泄漏


我已经使用Reflector检查了代码,并且在AppBuilderExtensions.CreatePerOwinContext()创建的对象的
Dispose()
方法中设置了一个断点。我冒昧地将此转发给处理此代码的Microsoft团队。如果有他们的响应,我会很高兴听到。上次我听到,有人正在调查这个问题。主要的问题是身份代码不是开源的,很难创建一个请求来修复它。目前codeplex上有一个占位符,但是。。。还没有。这可能是一个暗示,它最终将是开源的。虽然观察到未处理一次性每个请求对象是一个bug,但缺少调用
Dispose
不会导致内存泄漏,至少不会导致托管内存泄漏<代码>处置
将确保释放非托管资源。托管内存由垃圾收集器释放,而不是通过调用
Dispose
释放。如果托管内存“泄漏”,则是因为OWIN框架永久存储对每个新
DbContext
UserManager
的引用,从而禁止垃圾收集器在请求结束时释放内存。这可能看起来很奇怪,但我想停留在OWIN抽象中,不想引用System.Web…这确实引入了对System.Web的依赖,但它确实有效!
public class ClassIWantOneInstancePerContext
{
     //other code...

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

        return TheInstance;
    }
}
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;
        }
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;
        }