2014-06-24 4 views
20

Uso OWIN + Microsoft.AspNet.Identity.Owin (v.2.0.0.0) nell'applicazione Web. Mi registro UserManager/DbContext per ogni richiesta web, come ampiamente raccomandato:Perdita di memoria in Owin.AppBuilderExtensions

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

ma non è mai disposto. Ho preso un colpo d'occhio guardare in riflettore e sembra come un insetto in metodo di estensione:

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 ha due callback - creare e smaltire, ma smaltirlo richiamata non è registrato qui. Ho anche confermato il mio sospetto con il profiler della memoria.

Non vedo Owin su codeplex/github (in realtà ho pensato che sia open source), quindi non so dove porre la mia domanda: potrebbe qualcun altro confermarlo è una perdita di memoria? Non sono sicuro perché Google non dice nulla al riguardo, mi aspetto che dovrebbe essere discusso ovunque, se si tratta di un bug.

+1

Mi sono permesso di inoltrarlo al team Microsoft che gestisce questo codice. –

+0

Sarebbe bello sentire la loro risposta, se presente. –

+0

Ultimo ho sentito, ci stavano indagando sul problema. Il problema principale è che il codice di identità non è open source ed è piuttosto difficile creare una richiesta di pull per risolverlo. C'è un segnaposto in codeplex al momento, ma ... ancora niente. Potrebbe essere un suggerimento che sarebbe open source alla fine. –

risposta

6

Ho anche il suo problema, niente di quello registrato con CreatePerOwinContext viene eliminato. Sto usando v2.1.

Ecco una soluzione temporanea che sta funzionando bene per me come un aggiramento fino a quando questa libreria non viene riparata. Hai sostanzialmente per registrare manualmente ciascuno dei tipi che utilizzano registri con CreatePerOwnContext nella classe seguente, e poi alla fine della vostra procedura di avvio si registra questa classe personalizzata:

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(); 
     } 
    } 
} 

Alla fine il vostro registro processo di avvio questa classe:

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

Ora tutto otterrà smaltiti alla fine della pipeline di richiesta correttamente.

1

È possibile impostare la logica per lo smaltimento delle istanze create con CreatePeOwinContext() nella stessa callback utilizzata per creare queste intenzioni. Cioè:

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

     //other code... 
    } 
} 

Poi si solo dovrebbe preoccuparsi di includere una chiamata a DisposeOnPipelineCompleted() all'interno del callback utilizzato per creare un'istanza di classe. Cioè:

public class ClassIWantOneInstancePerContext 
{ 
    //other code... 

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

     return TheInstance; 
    } 
} 

Inoltre, non dimenticare di includere il metodo Dispose() sulla definizione di classe!

+0

Questo può sembrare strano motivo, ma voglio rimanere all'interno dell'astrazione OWIN e non voglio fare riferimento a System.Web ... –

+0

Questo introduce un dipen- dente su System.Web, ma funziona sicuramente! – ilikeprogramming

0

Usage: app.CreatePerRequest<AuthorizationContext>();

Metodo di estensione:

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

utilizzare Dependency Injection è necessario configurare Owin: app.UseScopePerOwinRequest(_dependencyResolver); - Questo dovrebbe essere il primo middleware ..

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

E per sopra codice per lavorare devi implementare IDepedencyResolver con qualsiasi contenitore.

  • La richiesta arriva e nuove possibilità per la richiesta di ottenere creato
  • In tale ambito si crea un altro oggetto.
  • Utilizzare quegli oggetti in altri middleware
  • e quando l'ambito è finito, viene eliminato.
  • anche gli oggetti all'interno di tale ambito che non sono disposti vengono eliminati.
5

La perdita di memoria in AppBuilderExtensions classe è stata già fissata nella versione più recente di Microsoft.AspNet.Identity.Owin libreria (2.2.1).

Ho controllato il codice utilizzando Reflector e anche inserendo un punto di interruzione nei metodi Dispose() creati da AppBuilderExtensions.CreatePerOwinContext().