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