gpt4 book ai didi

c# - 具有存储库和工作单元的 ASP.NET 标识

转载 作者:IT王子 更新时间:2023-10-29 04:05:13 25 4
gpt4 key购买 nike

我正在使用 Entity Framework 6 学习 ASP.NET MVC 5 应用程序中的存储库和工作单元模式。

我已经看了很多教程和文章,但几乎所有的都是自相矛盾的。一些人说存储库和工作单元模式很好,其他人说 DbContext 已经是一个存储库和工作单元,其他人说类似的话,但提供了一种完全不同的方法。我尝试了所有这些不同的方法(好吧,也许不是所有的方法),但仍在纠结于哪种方法是最正确的。

我目前拥有的是:

  • IRepository 和 GenericRepository 实现 IRepository
  • IUnitOfWork 和 UnitOfWork 实现 IUnitOfWork
  • IDbContext 和 MyDbContext 继承自 IdentityDbContext 并实现了 IDbContext

不确定我是否需要为它粘贴代码,我认为它非常通用,问题实际上不在于 Repository/UnitOfWork 本身。我遇到的问题是将 ASP.NET 标识类与我的存储库和工作单元结合使用。我为成员(member)和所有其他数据共享同一个数据库——我认为这是一种常见的情况。我找不到好的解决方案,如何使用我的存储库实例化 ASP.NET Identity 类。

UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(_DBCONTEXT_);
this.UserManager = new UserManager<ApplicationUser>(store);

我应该用什么代替 DBCONTEXT,以便它与我的 UnitOfWork 共享同一个 DbContext?或者如何以其他方式使 ASP.NET Identity 与 UnitOfWork 一起工作?

我尝试将 DbContext 公开为 UnitOfWork 类的公共(public)属性,例如:

UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(this.unitOfWork.MyDbContext);

但我认为它不对 - 它不适用于自定义 IDbContext 接口(interface),并且使代码不利于单元测试。

我还尝试实现 CustomUserStore 和 CustomRoleStore - 总的来说它是有效的,但是当我测试它时,它需要实现越来越多的方法。这个解决方案看起来太复杂了 - 我真的希望有更简单的方法。

最佳答案

我发现使用 ASP.Net Identity 2.0 和 EF6 有点挑战。最大的缺点是缺少文档或文档冲突。

我正在使用 WebApi 2.0、EF6 和 ASP.Net Identity 2.0。起初很难开始,但一旦开始工作,就很好了。

我创建了自己的身份类。目前我不关心扩展身份类我只想生成表并登录到系统。

自定义角色

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

自定义用户声明

public class CustomUserClaim : IdentityUserClaim<int> { }

自定义用户登录

public class CustomUserLogin : IdentityUserLogin<int> { }

自定义用户角色

public class CustomUserRole : IdentityUserRole<int> {}

用户

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

}

我不喜欢 Identity 表的命名,所以我更改了名称。

数据上下文

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");

}
}

我发现获取 UserManager 有点麻烦。

我创建了一个静态类来处理它。 UserStore 确实处理 DataContext 的生命周期,但您必须调用 dispose 才能发生这种情况。如果您在别处使用此 DataContext 引用,这可能会导致问题。我最终会将它连接到我的 DI 容器中,但现在这就是我所拥有的:

public class Identity
{
/// <summary>
/// Gets the user manager.
/// </summary>
/// <returns>UserManager&lt;User, System.Int32&gt;.</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;
}
}

我的大部分数据访问都使用工作单元模式。它运作良好。在某些情况下,我的数据需要比我公开 DataContext 的工作单元公开的数据更多的控制权。如果这对我仍然不起作用,我将回退到使用存储库。

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&lt;T&gt;.</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&lt;T&gt;.</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&lt;T&gt;.</returns>
public DbSqlQuery<T> ExecuteQueryCommand<T>(string sql) where T : class
{
return _unitOfWork.ExecuteQueryCommand<T>(sql);
}
}

以下是它的一些实际应用示例。我有 nHibernate 背景,喜欢在 using 范围内定义一个事务,所以我在我的工作单元中实现了。

        using (var trans = _unitOfWork.BeginTransaction())
{
var newAgency = trans.Insert(new Database.Schema.Agency() { Name = agency.Name, TaxId = agency.TaxId });

}

另一个使用“查找”关闭工作单元的示例:

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

用户创建和用户登录

我使用 ASP.NET Identity 进行登录和用户创建,并使用我的工作单元进行其他所有操作。

测试

我不会尝试测试 ASP.NET Identity。首先,我确信微软在测试它方面做得很好。我相信他们比你或我做得更好。如果您真的想围绕 ASP.NET Identity 代码进行测试,请将其放在接口(interface)后面并模拟接口(interface)。

关于c# - 具有存储库和工作单元的 ASP.NET 标识,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23226140/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com