/ / Przenoszenie ApplicationUser i innych modeli poza projekt MVC - asp.net-mvc, architektura, asp.net-identity-2

Przenoszenie aplikacji i innych modeli z projektu MVC - asp.net-mvc, architektura, asp.net-identity-2

Jak mogę oddzielić właściwości, funkcje i klasy z domyślnego ASP.Net Mvc / Identity 2.0? Walczę z kilkoma rzeczami:

  • domyślnie chce użyć kontekstu OWIN do połączenia jakiegoś rodzaju iniekcji zależności i kontrolowania menedżerów
  • umieszcza ApplicationDbContext na poziomie aplikacji, gdzie moja architektura wymaga, aby był dostępny na „niższych” poziomach.
  • Wymaga, abym zadeklarował wszystkie właściwości w tej samej klasie co funkcjonalność, która działa na te właściwości (co nie pasuje do mojej architektury)
  • Model ApplcationUser ma zależności od Asp.Net, które chciałbym przerwać, jeśli mam przenieść POCO na warstwę rozwiązania inną niż MVC

Architektura aplikacji:

Mam rozwiązanie, które ma kilka poziomów:

  • API - definiuje interfejsy dla usług
  • Domena - przechowuje modele POCO reprezentujące domenę biznesową
  • Biznes - przechowuje logikę do interakcji z obiektami domeny i zużywa usługi
  • Usługa - implementacja usług, w tym Entity Framework oraz map struktur dla obiektów domeny
  • Aplikacja - w tym przypadku aplikacja MVC.

Moja warstwa biznesowa wie tylko o interfejsach usług, a nie o implementacji, i używam wstrzykiwania zależności, aby wszystko połączyć.

Mam kilka interfejsów definiujących operacje odczytu / zapisu / jednostki pracy dla usługi danych oraz implementację tych, które dziedziczą po DbContext (w mojej warstwie usług). Zamiast serii plików DbSet<MyPoco> MyPocos {get;set;}, Łączę go, przekazując serię konfiguracji typów, które definiują relacje, a następnie uzyskując dostęp do moich typów Set<Type>(). Wszystko to działa świetnie.

Ten stos został utworzony dla istniejącej aplikacji i działa dobrze. Wiem, że przejdzie do aplikacji MVC i mam problemy tylko z „gotowym do użycia” ASP.Net Identity-2.

Odpowiedzi:

7 dla odpowiedzi № 1

Moim rozwiązaniem było: Abstrakcyjne wszystkie rzeczy

Poradziłem sobie z tym, abstrahując większość funkcjonalności tożsamości we własnym projekcie, co pozwoliło na łatwiejsze testowanie jednostkowe i ponowne wykorzystanie abstrakcji w innych projektach.

Wpadłem na pomysł po przeczytaniu tego artykułu

Tożsamość ASP.NET z ignorowaniem trwałości z wzorcami

Następnie dostosowałem pomysł do moich potrzeb.Po prostu wymieniłem wszystko, czego potrzebowałem z asp.net.identity na moje niestandardowe interfejsy, które mniej lub bardziej odzwierciedlały funkcjonalność zapewnianą przez framework, ale z zaletą łatwiejszej abstrakcji, a nie implementacji.

IIdentityUser

/// <summary>
///  Minimal interface for a user with an id of type <seealso cref="System.String"/>
/// </summary>
public interface IIdentityUser : IIdentityUser<string> { }
/// <summary>
///  Minimal interface for a user
/// </summary>
public interface IIdentityUser<TKey>
where TKey : System.IEquatable<TKey> {
TKey Id { get; set; }
string UserName { get; set; }
string Email { get; set; }
//...other code removed for brevity
}

IIdentityManager

/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager : IIdentityManager<IIdentityUser> { }
/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager<TUser> : IIdentityManager<TUser, string>
where TUser : class, IIdentityUser<string> { }
/// <summary>
/// Exposes user related api which will automatically save changes to the UserStore
/// </summary>
public interface IIdentityManager<TUser, TKey> : IDisposable
where TUser : class, IIdentityUser<TKey>
where TKey : System.IEquatable<TKey> {
//...other code removed for brevity
}

IIdentityResult

/// <summary>
/// Represents the minimal result of an identity operation
/// </summary>
public interface IIdentityResult : System.Collections.Generic.IEnumerable<string> {
bool Succeeded { get; }
}

W mojej domyślnej implementacji menedżera tożsamości, która również żyje w swoim własnym projekcie, po prostu zapakowałem plik ApplicationManager a następnie zamapowano wyniki i funkcje między moimi typami i typami asp.net.identity.

public class DefaultUserManager : IIdentityManager {
private ApplicationUserManager innerManager;

public DefaultUserManager() {
this.innerManager = ApplicationUserManager.Instance;
}
//..other code removed for brevity
public async Task<IIdentityResult> ConfirmEmailAsync(string userId, string token) {
var result = await innerManager.ConfirmEmailAsync(userId, token);
return result.AsIIdentityResult();
}
//...other code removed for brevity
}

Warstwa aplikacji jest świadoma tylko abstrakcji, a implementacja jest konfigurowana podczas uruchamiania. Nie mam żadnych using Microsoft.AspNet.Identity na wyższym poziomie, ponieważ wszyscy używają lokalnych abstrakcji.

poziomy mogą wyglądać następująco:

  • API - definiuje interfejsy dla usług (w tym interfejsy do abstrakcji tożsamości)
  • Domena - przechowuje modele POCO reprezentujące domenę biznesową
  • Biznes - przechowuje logikę do interakcji z obiektami domeny i zużywa usługi
  • Usługa - implementacja usług, w tym Entity Framework oraz map struktur dla obiektów domeny
  • Identity - implementacja usług specyficznych dla Microsoft.AspNet.Identity, w tym Microsoft.AspNet.Identity.EntityFramework; i konfiguracja OWIN
  • Aplikacja - w tym przypadku aplikacja MVC.

W warstwie aplikacji MVC plik AccountController w związku z tym tylko potrzebne

using MyNamespace.Identity.Abstractions

public partial class AccountController : Controller {
private readonly IIdentityManager userManager;

public AccountController(IIdentityManager userManager) {
this.userManager = userManager;
}

//...other code removed for brevity

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Signin(LoginViewModel model, string returnUrl) {
if (ModelState.IsValid) {
// authenticate user
var user = await userManager.FindAsync(model.UserName, model.Password);
if (user != null) {
//...code removed for brevity
} else {
// login failed
setFailedLoginIncrementalDelay();
ModelState.AddModelError("", "Invalid user name or password provided.");
}
}
//TODO: Audit failed login

// If we got this far, something failed, redisplay form
return View(model);
}
}

Zakłada się, że używasz jakiegoś frameworka DI.Tylko w konfiguracji IoC wspomina się o warstwie, która implementuje tożsamość, całkowicie abstrahując ją od tych, którzy potrzebują tożsamości.

//NOTE: This is custom code.
protected override void ConfigureDependencies(IContainerBuilder builder) {
if (!builder.HasHandler(typeof(IIdentityManager))) {
builder.PerRequest<IIdentityManager, DefaultUserManager>();
}
}