Ho trovato che lavorare con ASP.Net Identity 2.0 ed EF6 è un po 'impegnativo. Il più grande svantaggio è la mancanza di documentazione o documentazione conflittuale.
Utilizzo WebApi 2.0, EF6 e ASP.Net Identity 2.0. All'inizio è stato difficile andare avanti, ma una volta che funziona, è stato buono.
Ho creato le mie proprie classi di identità. Al momento non mi interessa estendere le classi di identità. Voglio solo generare le tabelle e accedere al sistema.
CustomRole
public class CustomRole : IdentityRole<int, CustomUserRole>
{
/// <summary>
/// Initializes a new instance of the <see cref="CustomRole"/> class.
/// </summary>
public CustomRole() { }
/// <summary>
/// Initializes a new instance of the <see cref="CustomRole"/> class.
/// </summary>
/// <param name="name">The name.</param>
public CustomRole(string name) { Name = name; }
}
CustomUserClaim
public class CustomUserClaim : IdentityUserClaim<int> { }
CustomUserLogin
public class CustomUserLogin : IdentityUserLogin<int> { }
CustomUserRole
public class CustomUserRole : IdentityUserRole<int> {}
utente
public class User : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
/// <summary>
/// Gets or sets the first name.
/// </summary>
/// <value>The first name.</value>
public string FirstName { get; set; }
/// <summary>
/// Gets or sets the last name.
/// </summary>
/// <value>The last name.</value>
public string LastName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="User"/> is active.
/// </summary>
/// <value><c>true</c> if active; otherwise, <c>false</c>.</value>
public bool Active { get; set; }
}
non mi piace la denominazione delle tabelle di identità, così ho cambiato i nomi.
DataContext
public class DataContext : IdentityDbContext<User, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public DataContext() : base("DefaultConnection"){}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<CustomUserRole>().ToTable("UserRoles", "Security");
modelBuilder.Entity<CustomUserLogin>().ToTable("UserLogins", "Security");
modelBuilder.Entity<CustomUserClaim>().ToTable("UserClaims", "Security");
modelBuilder.Entity<CustomRole>().ToTable("Roles", "Security");
modelBuilder.Entity<User>().ToTable("Users", "Security");
}
}
ho trovato ricevendo l'UserManager un po 'di dolore.
Ho creato una classe statica per gestirlo. Lo UserStore gestisce il ciclo di vita di DataContext, ma dovrai chiamare dispose perché ciò accada. Ciò potrebbe causare problemi se si sta utilizzando questa referenza DataContext altrove. Io alla fine collegarlo nel mio container DI, ma per ora questo è quello che ho:
public class Identity
{
/// <summary>
/// Gets the user manager.
/// </summary>
/// <returns>UserManager<User, System.Int32>.</returns>
public static UserManager<User, int> GetUserManager()
{
var store = new UserStore<User, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>(new DataContext());
var userManager = new UserManager<User, int>(store);
return userManager;
}
}
io uso l'Unità di modello di lavoro per la maggior parte il mio accesso ai dati. Funziona bene. Ci sono alcuni casi in cui ho dati che richiedono un controllo maggiore di quello che l'unità di lavoro espone per questi casi che ho esposto DataContext. Se ciò non funziona ancora per me, ricado all'utilizzo di un repository.
public class UnitOfWork : IUnitOfWork
{
private readonly IContainer _container;
public UnitOfWork(IContainer container) :this()
{
_container = container;
}
//private readonly List<CommitInterception> _postInterceptions = new List<CommitInterception>();
public DataContext Context { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="UnitOfWork"/> class.
/// </summary>
public UnitOfWork()
{
Context = new DataContext();
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <exception cref="System.NotImplementedException"></exception>
public void Dispose()
{
//Chuck was here
try
{
Commit();
}
finally
{
Context.Dispose();
}
}
/// <summary>
/// Begins the transaction.
/// </summary>
/// <returns>IUnitOfWorkTransaction.</returns>
public IUnitOfWorkTransaction BeginTransaction()
{
return new UnitOfWorkTransaction(this);
}
/// <summary>
/// Commits this instance.
/// </summary>
public void Commit()
{
Commit(null);
}
/// <summary>
/// Commits transaction.
/// </summary>
public void Commit(DbContextTransaction transaction)
{
//Lee was here.
try
{
Context.SaveChanges();
if (transaction != null)
{
transaction.Commit();
}
//foreach (var interception in _postInterceptions)
//{
// interception.PostCommit(interception.Instance, this);
//}
}
catch (DbEntityValidationException ex)
{
var errors = FormatError(ex);
throw new Exception(errors, ex);
}
catch
{
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
finally
{
// _postInterceptions.Clear();
}
}
/// <summary>
/// Formats the error.
/// </summary>
/// <param name="ex">The ex.</param>
/// <returns>System.String.</returns>
private static string FormatError(DbEntityValidationException ex)
{
var build = new StringBuilder();
foreach (var error in ex.EntityValidationErrors)
{
var errorBuilder = new StringBuilder();
foreach (var validationError in error.ValidationErrors)
{
errorBuilder.AppendLine(string.Format("Property '{0}' errored:{1}", validationError.PropertyName, validationError.ErrorMessage));
}
build.AppendLine(errorBuilder.ToString());
}
return build.ToString();
}
/// <summary>
/// Inserts the specified entity.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity">The entity.</param>
/// <returns>``0.</returns>
public T Insert<T>(T entity) where T: class
{
var instance = _container.TryGetInstance<IUnitOfWorkInterception<T>>();
if (instance != null)
{
instance.Intercept(entity, this);
// _postInterceptions.Add(new CommitInterception() { Instance = entity, PostCommit = (d,f) => instance.PostCommit(d as T, f) });
}
var set = Context.Set<T>();
var item = set.Add(entity);
return item;
}
public T Update<T>(T entity) where T : class
{
var set = Context.Set<T>();
set.Attach(entity);
Context.Entry(entity).State = EntityState.Modified;
return entity;
}
/// <summary>
/// Deletes the specified entity.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity">The entity.</param>
public void Delete<T>(T entity) where T : class
{
var set = Context.Set<T>();
set.Remove(entity);
}
/// <summary>
/// Finds the specified predicate.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="predicate">The predicate.</param>
/// <returns>IQueryable{``0}.</returns>
public IQueryable<T> Find<T>(Expression<Func<T, bool>> predicate) where T : class
{
var set = Context.Set<T>();
return set.Where(predicate);
}
/// <summary>
/// Gets all.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>IQueryable{``0}.</returns>
public IQueryable<T> GetAll<T>() where T : class
{
return Context.Set<T>();
}
/// <summary>
/// Gets the by identifier.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="id">The identifier.</param>
/// <returns>``0.</returns>
public T GetById<T>(int id) where T : class
{
var set = Context.Set<T>();
return set.Find(id);
}
/// <summary>
/// Executes the query command.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sql">The SQL.</param>
/// <returns>DbSqlQuery{``0}.</returns>
public DbSqlQuery<T> ExecuteQueryCommand<T>(string sql) where T : class
{
var set = Context.Set<T>();
return set.SqlQuery(sql);
}
private class CommitInterception
{
public object Instance { get; set; }
public Action<object, IUnitOfWork> PostCommit { get; set; }
}
}
public class UnitOfWorkTransaction : IUnitOfWorkTransaction
{
private readonly UnitOfWork _unitOfWork;
private readonly DbContextTransaction _transaction;
/// <summary>
/// Initializes a new instance of the <see cref="UnitOfWorkTransaction"/> class.
/// </summary>
/// <param name="unitOfWork">The unit of work.</param>
public UnitOfWorkTransaction(UnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
_transaction = _unitOfWork.Context.Database.BeginTransaction();
Context = unitOfWork.Context;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
_unitOfWork.Commit(_transaction);
}
public DataContext Context { get; set; }
/// <summary>
/// Commits this instance.
/// </summary>
public void Commit()
{
_unitOfWork.Commit();
}
/// <summary>
/// Rollbacks this instance.
/// </summary>
public void Rollback()
{
_transaction.Rollback();
}
/// <summary>
/// Inserts the specified entity.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity">The entity.</param>
/// <returns>T.</returns>
public T Insert<T>(T entity) where T : class
{
return _unitOfWork.Insert(entity);
}
/// <summary>
/// Updates the specified entity.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity">The entity.</param>
/// <returns>T.</returns>
public T Update<T>(T entity) where T : class
{
return _unitOfWork.Update(entity);
}
/// <summary>
/// Deletes the specified entity.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="entity">The entity.</param>
public void Delete<T>(T entity) where T : class
{
_unitOfWork.Delete(entity);
}
/// <summary>
/// Finds the specified predicate.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="predicate">The predicate.</param>
/// <returns>IQueryable<T>.</returns>
public IQueryable<T> Find<T>(Expression<Func<T, bool>> predicate) where T : class
{
return _unitOfWork.Find(predicate);
}
/// <summary>
/// Gets all.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>IQueryable<T>.</returns>
public IQueryable<T> GetAll<T>() where T : class
{
return _unitOfWork.GetAll<T>();
}
/// <summary>
/// Gets the by identifier.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="id">The identifier.</param>
/// <returns>T.</returns>
public T GetById<T>(int id) where T : class
{
return _unitOfWork.GetById<T>(id);
}
/// <summary>
/// Executes the query command.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sql">The SQL.</param>
/// <returns>DbSqlQuery<T>.</returns>
public DbSqlQuery<T> ExecuteQueryCommand<T>(string sql) where T : class
{
return _unitOfWork.ExecuteQueryCommand<T>(sql);
}
}
Ecco alcuni esempi in azione. Ho uno sfondo ibernato e mi piace definire una transazione nell'ambito di uno using
quindi l'ho implementato nella mia unità di lavoro.
using (var trans = _unitOfWork.BeginTransaction())
{
var newAgency = trans.Insert(new Database.Schema.Agency() { Name = agency.Name, TaxId = agency.TaxId });
}
Un altro esempio di utilizzo del "Trova" fuori della unità di lavoro:
var users = _unitOfWork.Find<Database.Schema.User>(s => s.Active && s.Agency_Id == agencyId)
.Select(u=> new {Label = u.FirstName + " " + u.LastName, Value = u.Id})
.ToList();
creazione di utenti e utenti Sign-In
Io uso ASP.NET identità per la accesso e creazione degli utenti e la mia unità di lavoro per tutto il resto.
Testing
non vorrei provare a testare ASP.NET identità. Per uno sono sicuro che Microsoft ha fatto un buon lavoro testandolo. Sono sicuro che hanno fatto un lavoro migliore di quello che tu o io potevamo fare. Se vuoi veramente testare il codice di identità di ASP.NET, mettilo dietro un'interfaccia e prendi in giro l'interfaccia.
' DbContext' è _a_ repository - se si sceglie di usarlo come repository _the_ allora siete tightly-. accoppiato a EF e prevenire qualsiasi tipo di unità di test su classi che richiedono un repository. –
Penso che questo articolo sia molto chiaro http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using -mvc-4/implementazione-the-repository-and-unit-of-work-patterns-in-un-asp-net-mvc-application –
Questo è stato il primo articolo che stavo attraversando. È stato davvero utile e ben scritto , ma .. non c'era nulla di menzionato riguardo il mio problema principale su come farlo funzionare con l'identità di ASP.NET. Alla fine ho dovuto passare un'istanza separata di DbContext per UserManager, che è in conflitto con il concetto di UnitOfWork. – Aleksanderis