2014-10-29 10 views
43

Sono un novizio in ASP.NET e sto imparando l'identità di ASP.NET. So che è basato sull'implementazione OWIN di Microsoft e sto ancora imparando anche questo. Quindi, mi sono imbattuto nel metodo di estensione CreatePerOwinContext nel codice di avvio di Owin, e non vedo uno scopo chiaro di usarlo. È una specie di contenitore per l'iniezione di dipendenza? Qual è il vero scopo del metodo? In quale caso dovrebbe essere applicato?Qual è lo scopo del metodo di estensione CreatePerOwinContext nell'implementazione OWIN di Microsoft

risposta

60

CreatePerOwinContext registra un callback statico che verrà utilizzato dall'applicazione per ottenere una nuova istanza di un tipo specificato.
Questa richiamata verrà richiamata una sola volta per richiesta e memorizzerà l'oggetto/gli oggetti in OwinContext in modo da poterli utilizzare in tutta l'applicazione.

Diciamo che avete definito una propria implementazione di IdentityDbContext:

public class ApplicationDatabaseContext : IdentityDbContext<MyApplicationUser, MyRole, Guid, MyUserLogin, MyUserRole, MyUserClaim> 
{ 
    public ApplicationDatabaseContext() : base("<connection string>") 
    { 
    } 

    public static ApplicationDatabaseContext Create() 
    { 
     return new ApplicationDatabaseContext(); 
    } 

     protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder) 
     { 
     base.OnModelCreating(modelBuilder); 

     // Customize your table creation here. 

      #region USERS - INFOS 

     modelBuilder.Entity<UserInfo>() 
      .Property(p => p.FirstName) 
      .HasColumnType("varchar") 
      .HasMaxLength(70); 

     modelBuilder.Entity<UserInfo>() 
      .Property(p => p.LastName) 
      .HasColumnType("varchar") 
      .HasMaxLength(70); 

     modelBuilder.Entity<UserInfo>() 
      .Property(p => p.Address) 
      .HasColumnType("varchar") 
      .HasMaxLength(100); 

     modelBuilder.Entity<UserInfo>() 
      .Property(p => p.City) 
      .HasColumnType("varchar") 
      .HasMaxLength(100); 

     modelBuilder.Entity<UserInfo>() 
      .ToTable("UsersInfo"); 

     #endregion 
     } 

     public DbSet<UserInfo> UsersInfo { get; set; } 
} 

e l'implementazione di UserManager:

public class ApplicationUserManager : UserManager<MyApplicationUser, Guid> 
{ 
    public ApplicationUserManager(IUserStore<MyApplicationUser, Guid> store) : base(store) 
     { 
     } 

     public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
     { 
      var manager = new ApplicationUserManager(new MyUserStore(context.Get<ApplicationDatabaseContext>())); 

      manager.UserValidator = new UserValidator<MyApplicationUser, Guid>(manager) 
      { 
       AllowOnlyAlphanumericUserNames = false, 
       RequireUniqueEmail = true 
      }; 

      manager.PasswordValidator = new PasswordValidator() 
      { 
       RequiredLength = 6, 
       RequireNonLetterOrDigit = false,  
       // RequireDigit = true, 
       RequireLowercase = false, 
       RequireUppercase = false, 
      }; 

      var dataProtectionProvider = options.DataProtectionProvider; 

      if (dataProtectionProvider != null) 
      { 
       manager.UserTokenProvider = new DataProtectorTokenProvider<MyApplicationUser, Guid>(dataProtectionProvider.Create("PasswordReset")); 
      } 

      return (manager); 
     } 
} 

Nella tua Owin avvio si registrare il callback:

// IAppBuilder app 

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

che chiamerà il metodo statico:

public static ApplicationDatabaseContext Create() 
{ 
    return new ApplicationDatabaseContext(); 
} 

e

public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
{ 
    ... 
} 

Ora sarete in grado di accedere al contesto di database e User Manager in modo semplice semplice:

ApplicationDatabaseContext dbContext = context.OwinContext.Get<ApplicationDatabaseContext>(); 
ApplicationUserManager userManager = context.OwinContext.GetUserManager<ApplicationUserManager>(); 

In il tuo ApiController (se stai usando WebApi):

IAuthenticationManager authenticationManager = HttpContext.Current.GetOwinContext().Authentication; 
ApplicationUserManager applicationUserManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); 
+3

Grazie per la spiegazione. Quindi sembra una specie di localizzatore di servizi di sistema. Nel codice di avvio, uno registra un factory di servizi fornendo un metodo statico che crea il servizio quando viene richiesto per la prima volta e da qualche parte nell'applicazione il servizio può essere recuperato da OwinContext –

+2

@NextDeveloper: Esatto. Ovviamente, se stai utilizzando il tuo IoC, puoi saltare quella registrazione e utilizzare la tua [implementazione] (https://aspnetidentity.codeplex.com/discussions/532294) – LeftyX

+1

@NextDeveloper: se sei soddisfatto del mio risposta potresti, per favore, accettarlo? Saluti. – LeftyX

1

è possibile utilizzare typeof per ottenere il nome in questo modo:

HttpContext.GetOwinContext().Get<ApplicationDbContext>(typeof(ApplicationDbContext).ToString()); 
3

Qual è il vero scopo del metodo? In quale caso dovrebbe essere applicato ?

Per rispondere alla tua domanda più direttamente, questo è inutile.

  1. È una sorta di fabbrica IoC, che alcune persone amano usare.
  2. Questo ti fa usare il loro (IoC) sulla tua scelta.
  3. (Non mi piace IoC, sembra un anti-pattern per le persone che vogliono sentirsi calde e confuse e usare il termine "architettura".)
  4. Ma seriamente, questo modello non ha interfacce IoC, Le funzioni di fabbrica statiche IoC! Chi è stata l'idea? Perché non usare da solo la funzione Factory? Ora devi ricordare (Google) una chiamata API aggiuntiva e quando premi F12 su Get, non ti servirai da nessuna parte.

Cosa si dovrebbe fare invece?

Personalmente, sono un fan dell'utilizzo di OO per questo, ricorda OO? La fattoria Pepperidge ricorda. Con OO, si mantiene il controllo, è possibile eseguire il debug, il registro e l'estensione.

public class BaseApiController : ApiController 
{ 
    private AppDbContext _db = null; 

    protected AppDbContext db 
    { 
     get 
     { 
      if (_db == null) 
      { 
       _db = AppDbContext.Create(); //Hey look a proper factory that you can extend with other overloads! And I can debug this line - neat! 
      } 
      return _db; 
     } 

    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      if (_db != null) 
       _db.Dispose(); 
     } 
    } 

} 

Tutto questo potrebbe essere una perdita di tempo, se qualcuno trova qualche documentazione perché gli ingegneri di Microsoft messo questo, potrebbero avere una buona ragione per cui, ma ne dubito, quindi cerchiamo di upvote questa risposta nel frattempo.

UPDATE

Ecco il perché, perché è lì per Microsoft: https://blogs.msdn.microsoft.com/webdev/2014/02/12/per-request-lifetime-management-for-usermanager-class-in-asp-net-identity/

In sostanza, l'UserManager e tutti loro sono costruiti per questo tipo di struttura. I controlli di sicurezza si verificano nella pipeline, quindi perché non avere un singleton collegato alla richiesta, per ridurre gli sprechi? Perché è nascosto.

Vorrei ancora consigliare la creazione della propria istanza del contesto db su una cella di base, che rende molto più pulito l'utilizzo. Se lo desideri davvero, puoi avere una proprietà nella tua baseclass che recupera il singleton da OwinContext.

Quanto tempo sprechiamo cercando di capire queste API di fantasia, e autorizzare gli attributi e simili, quando tutto quello che vogliamo fare è:

public void DoSomething() 
{ 
    DemandAuthenticated(); 
    DemandAuthorised(typeof(somethingClass), "DoSomething"); 
} 

Chiaramente, io preferisco il codice verbose si può vedere.