Owin.AppBuilderExtensions中的内存泄漏

时间:2014-06-24 05:22:07

标签: 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);

但两者都没有处理过。我瞥了一眼反射器,它似乎是扩展方法中的一个错误:

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回调。我也证实了我对记忆探测器的怀疑。

我没有在codeplex / github上看到Owin(实际上我认为它是开源的),所以我不知道在哪里问我的问题:还有其他人可以确认这是内存泄漏吗? 我不确定,因为谷歌没有说它,我希望它应该在任何地方讨论,如果这是一个错误。

4 个答案:

答案 0 :(得分:6)

我也有他的问题,在CreatePerOwinContext中注册的任何内容都没有被处理掉。我正在使用v2.1。

这是一个临时修复,对我来说很有效,直到修复了这个lib。您基本上必须在以下类中手动注册使用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));

现在一切都将在请求管道的末尾正确处理。

答案 1 :(得分:5)

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

我已经使用Reflector检查了代码,并在Dispose()创建的对象的AppBuilderExtensions.CreatePerOwinContext()方法中添加了断点。

答案 2 :(得分:1)

您可以将用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()方法!

答案 3 :(得分:0)

用法: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。

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