让 IdentityManager 对两个不同的数据库进行更改



我们有两个数据库,其中包含如下所示的用户表:

  • MasterDB.AspNetUsers
  • Customer1DB.AspNetUsers

我们使用MasterDb登录用户,然后将他们连接到客户数据库,例如Customer1DB(或其他一些客户数据库,具体取决于用户)。两个用户表具有基于 ASP.NET 身份框架的相同模式,我们可以使用 IdentityManager 来管理 MasterDB 上的用户。

现在我想要的是对 MasterDb 用户记录执行的任何更改,将其镜像到客户数据库上的用户表(具有相同的用户 ID)。我想知道最好的方法是什么?我是否需要修改对UserStoreRoleStoreUserManagerRoleManager的所有操作?我有一个函数可以获取 UserId 并将其从第一个数据库添加或更新到第二个数据库,但我不确定我应该如何将其集成到身份框架实现中。

谢谢!

我认为您有 2 个选择:

  1. 为所需的存储和功能创建适配器。适配器将在两个数据库上调用相同的方法。

  2. 在主数据库上下文中,当您保存更改时,您可以查看更改跟踪器并将更改复制到另一个数据库上下文。

每种方法都有优点和缺点。

1

适配器示例:

class MultiDatabaseUserStore<T> : IUserStore<T>
    where T : IdentityUser
{
    private readonly UserStore<T>[] stores;
    public MultiDatabaseUserStore(params IdentityDbContext[] dbContexts)
    {
        if (dbContexts == null || dbContexts.Length <= 0)
        {
            throw new ArgumentException("At least one db context is required.", "dbContexts");
        }
        this.stores = dbContexts.Select(x => new UserStore<T>(x)).ToArray();
    }
    public void Dispose()
    {
        foreach (var store in this.stores)
        {
            store.Dispose();
        }
    }
    public Task CreateAsync(T user)
    {
        return this.ExecuteOnAll(x => x.CreateAsync(user));
    }
    public Task UpdateAsync(T user)
    {
        return this.ExecuteOnAll(x => x.UpdateAsync(user));
    }
    public Task DeleteAsync(T user)
    {
        return this.ExecuteOnAll(x => x.DeleteAsync(user));
    }
    public Task<T> FindByIdAsync(string userId)
    {
        return this.stores.First().FindByIdAsync(userId);
    }
    public Task<T> FindByNameAsync(string userName)
    {
        return this.stores.First().FindByNameAsync(userName);
    }
    private Task ExecuteOnAll(Func<UserStore<T>, Task> function)
    {
        return Task.WhenAll(this.stores.Select(function));
    }
}

它像这样集成:

var store = new MultiDatabaseUserStore<IdentityUser>(new MasterDb(), new Customer1DB());
var userManager = new UserManager<IdentityUser>(store);

这有效,但根据您的需求,您将有更多的接口来实现:

  • IUserLoginStore
  • IUserClaimStore
  • IUserRoleStore
  • IUserPasswordStore
  • IUserSecurityStampStore
  • 智商用户商店
  • IUserEmailStore
  • 电话号码存储
  • IUser双因素存储
  • IUserLockoutStore

阿拉伯数字

示例主数据库上下文:

class MasterDb : IdentityDbContext
{
    public MasterDb()
    {
    }
    public override int SaveChanges()
    {
        var changes = this.GetChangesToReplicate();
        var i = base.SaveChanges();
        this.SaveReplicatedChanges(changes);
        return i;
    }
    public override Task<int> SaveChangesAsync()
    {
        return Task.Run(async () =>
        {
            var changes = this.GetChangesToReplicate();
            var i = await base.SaveChangesAsync();
            this.SaveReplicatedChanges(changes);
            return i;
        });
    }
    private void SaveReplicatedChanges(IEnumerable<Action<IdentityDbContext>> changes)
    {
        if (changes != null)
        {
            using (var db = new Customer1DB())
            {
                foreach (var change in changes)
                {
                    change(db);
                }
                db.SaveChanges();
            }
        }
    }
    private IEnumerable<Action<IdentityDbContext>> GetChangesToReplicate()
    {
        var actions = new List<Action<IdentityDbContext>>();
        var userTye = typeof(IdentityUser);
        var changedEntries = this.ChangeTracker.Entries();
        var users = changedEntries.Where(x => x.Entity.GetType() == userTye).ToList();
        foreach (var u in users)
        {
            switch (u.State)
            {
                case EntityState.Added:
                    var userToAdd = (IdentityUser)u.Entity;
                    actions.Add(db => db.Users.Add(userToAdd));
                    break;
                case EntityState.Modified:
                    var userToUpdate = (IdentityUser)u.Entity; ;
                    actions.Add(db =>
                    {
                        db.Users.Attach(userToUpdate);
                        db.Entry(userToUpdate).State = EntityState.Modified;
                    });
                    break;
                case EntityState.Deleted:
                    var userToDelete = (IdentityUser)u.Entity; ;
                    actions.Add(db =>
                    {
                        db.Users.Attach(userToDelete);
                        db.Entry(userToDelete).State = EntityState.Deleted;
                    });
                    break;
            }
        }
        return actions;
    }
}

这也有效,但仅限于用户实体。您必须添加角色、声明的复制,...根据您的需求。

2 改进

考虑一下,您可以跳过类型检查,只是将所有更改复制到另一个 DbContext。这大大简化了代码,并且适用于角色/声明。您还需要添加 bool 属性IsReplicatingChanges以便仅在必要时启用复制。

您只需要为身份存储创建一个实例,并将标志设置为 true

var dbContext = new MasterDb { IsReplicatingChanges = true };
var userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(dbContext));
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(dbContext));

GetChangesToReplicate()变成:

private IEnumerable<Action<IdentityDbContext>> GetChangesToReplicate()
{
    if (!this.IsReplicatingChanges) // Flag
    {
        // Return null when not activated
        return null;
    }
    var actions = new List<Action<IdentityDbContext>>();
    var changedEntries = this.ChangeTracker.Entries().Where(x => x.State != EntityState.Unchanged);
    foreach (var u in changedEntries)
    {
        var entity = u.Entity;
        var state = u.State;
        actions.Add(db =>
        {
            db.Set(entity.GetType()).Attach(entity);
            db.Entry(entity).State = state;
        });
    }
    return actions;
}

并且不要忘记将空检查添加到SaveReplicatedChanges()

相关内容

  • 没有找到相关文章

最新更新