【问题标题】:How can i commit in identity aspnetusers table and another table in one transaction我如何在一个事务中提交身份 aspnetusers 表和另一个表
【发布时间】:2021-03-23 21:26:03
【问题描述】:

我有两个表,一个是身份表 aspnetuser,另一个是我现在创建的用户表,aspnetuser 中的一列是自动生成的身份列,这是 aspnetuser 表中的外键和最终创建的用户表中的主键一对一的关系。 现在我有这个需要,我想同时在这两个表中插入,所以如果在 aspnetuser 中创建记录成功但在用户表中创建记录失败后,我需要回滚更改。 我怎样才能做到这一点?因为现在await _userManager.CreateAsync(user, Input.Password); 这段代码一被调用就进入 aspnetuser 表中的记录... 我在这里找到了类似的东西How to create transaction with asp.net identity? 但无法理解它,因为在我的情况下,aspnetuser 表有一个 U_ID 列,它是由 db 生成的标识列,我需要获取它的值才能在用户表中创建新记录 有可能吗? 这是图表 one-one relation

这是应用程序用户

  public class ApplicationUser : IdentityUser
    {
        [PersonalData]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int U_ID { get; set; }
    }

更新 正如@Zhi Lv 所说,我试过这个

returnUrl ??= Url.Content("~/");
                var result = false;
                var errorlist = new List<IdentityError>();
                ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
                var user = new ApplicationUser();
                if (ModelState.IsValid)
                {
                  
                        using (var transaction = _dbcontext.Database.BeginTransaction())
                        {
                            try
                            {
                                Guid guid = Guid.NewGuid();
                                ShortGuid sguid1 = guid;

                                //insert user to Users table.
                                var customeruser = new OnlineEarning.Core.DataModel.Models.User()
                                {
                                    Name = Input.UserName, 
                                    Password = Input.Password, 
                                    Email = Input.Email,
                                    UserRefNo = sguid1,
                                    EasyPaisaNoJazzCashNo = "2214457896",
                                    PhoneNo = "2214457896",
                                    City = "Rwp",

                                };
                                _dbcontext.Users.Add(customeruser);
                            _dbcontext.SaveChanges();
                                //get the latest user id.
                                var u_id = customeruser.Id;
                                //
                                user = new ApplicationUser { UserName = Input.Email, Email = Input.Email, U_ID = u_id };

                                var saveresult = await _userManager.CreateAsync(user, Input.Password);
                                if (saveresult.Succeeded)
                                    result = true;
                                else
                                    errorlist = saveresult.Errors.ToList();

                                transaction.Commit();
                            }
                            catch (Exception ex)
                            {
                                transaction.Rollback();
                            }
                        }
                      
                    if (result)
                    {
                        //do something
                    }
                    foreach (var error in errorlist)
                    {
                        ModelState.AddModelError(string.Empty, error.Description);
                    }
                }

                // If we got this far, something failed, redisplay form
                return Page();

但现在我得到了这个异常'Execution TImeout expired'

更新 2 现在我可以使用以下代码在单个事务中创建事务并在两个表中输入记录

  try
        {
            returnUrl ??= Url.Content("~/");
            var result = false;
            var errorlist = new List<IdentityError>();
            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
            var user = new ApplicationUser();

            if (ModelState.IsValid)
            {
                    using (TransactionScope transaction = new TransactionScope(System.Transactions.TransactionScopeAsyncFlowOption.Enabled))
                    {
                        try
                        {
                            Guid guid = Guid.NewGuid();
                            ShortGuid sguid1 = guid;

                            //insert user to Users table.
                            var customeruser = new OnlineEarning.Core.DataModel.Models.User()
                            {
                                Name = Input.UserName,
                                Password = Input.Password,
                                Email = Input.Email,
                                UserRefNo = sguid1,
                                EasyPaisaNoJazzCashNo = "01224457854",
                                PhoneNo = "01224457854",
                                City = "Rwp",

                            };
                            _dbcontext.Users.Add(customeruser);
                            _dbcontext.SaveChanges();
                            //get the latest user id.
                            var u_id = customeruser.Id;

                            user = new ApplicationUser { UserName = Input.Email, Email = Input.Email, U_ID = u_id };

                            var saveresult = await _userManager.CreateAsync(user, Input.Password);

                        if (saveresult.Succeeded)
                        {
                            result = true;
                            var uservs = await _userManager.FindByEmailAsync(Input.Email);
                            await _signInManager.SignInAsync(user, isPersistent: false);
                            var userv = await _userManager.FindByEmailAsync(Input.Email);
                        }
                        else
                            errorlist = saveresult.Errors.ToList();

                             transaction.Complete();
                       
                    }
                        catch (Exception ex)
                        {
                             transaction.Dispose();                   
                            ModelState.AddModelError(string.Empty, ex.InnerException.Message.ToString());
                    }

                    }
              
                if (result)
                {
                    //do something
                }
                foreach (var error in errorlist)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }
        catch(Exception ex)
        {
            return Page();
        }

但在调试过程中,我发现在调试中启动事务时,我无法读取或写入 sql server 中的两个表。现在我的问题是,如果两个用户尝试同时注册怎么办?只有一个其中一个是允许的,另一个会出现异常?如果是,那么在这种情况下该怎么办……离场交易是不能避免的,否则数据会不一致

【问题讨论】:

    标签: asp.net-core transactions asp.net-identity


    【解决方案1】:

    据我所知,每个表只能配置一列Identity,所以,在ApplicationUser类中,因为它继承自IdentityUser(它已经包含标识列),即使你配置U_ID为Identity,插入行时数据库不会生成值。另外,从图上看,好像是要配置aspnetusers表和Users表的一一对应关系,如果是这样的话,我建议你可以尝试在Users表中设置Identity列,使用以下代码配置关系:

    用户.cs:

    public class User
    {
        [Key] 
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int U_ID { get; set; }
        public string Name { get; set; }
        public string Password { get; set; }
        public string PhoneNo { get; set; }
        public string Email { get; set; }
        public ApplicationUser ApplicationUser { get; set; }
    }
    

    ApplicationUser.cs:

    public class ApplicationUser:IdentityUser
    {
        [PersonalData]
        public int U_ID { get; set; }
    
        [ForeignKey("U_ID")]
        public User User { get; set; }
    }
    

    ApplicationDbContext.cs:

    public class ApplicationDbContext : IdentityDbContext
    {
        public DbSet<ApplicationUser> ApplicationUsers { get; set; }
        #pragma warning disable CS0114 // Member hides inherited member; missing override keyword
        public DbSet<User> Users { get; set; }
        #pragma warning restore CS0114 // Member hides inherited member; missing override keyword
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
    

    那么,在注册用户的时候,你可以参考下面的代码来使用事务和插入数据。

    [AllowAnonymous]
    public class RegisterModel : PageModel
    {
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly UserManager<ApplicationUser> _userManager; 
        private readonly ApplicationDbContext _context;
        public RegisterModel( UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager,  ApplicationDbContext context )
        {
            _userManager = userManager;
            _signInManager = signInManager; 
            _context = context;
        } 
        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        { 
            var result = false;
            var errorlist = new List<IdentityError>();
            var user = new ApplicationUser();
            if (ModelState.IsValid)
            {
                using (var transaction = _context.Database.BeginTransaction())
                {
                    try
                    {
                        //insert user to Users table.
                        var customeruser = new User() { PhoneNo = "12345678", Name = "Tom", Password = "Password01", Email = "tom@hotmail.com" };
                        _context.Users.Add(customeruser);
                        _context.SaveChanges();
                        //get the latest user id.
                        var u_id = customeruser.U_ID;
                        //
                        user = new ApplicationUser { UserName = Input.Email, Email = Input.Email, U_ID = u_id };
    
                        var saveresult = await _userManager.CreateAsync(user, Input.Password); 
                        if (saveresult.Succeeded)
                            result = true;
                        else
                            errorlist = saveresult.Errors.ToList();
    
                        transaction.Commit();
                    }
                    catch (Exception)
                    {
                        transaction.Rollback();
                    }
                }
    
                if (result)
                { 
                    //do something
                }
                foreach (var error in errorlist)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }
    
            // If we got this far, something failed, redisplay form
            return Page();
        }
    }
    

    结果如下:

    参考:

    Entity Framework Core Transaction

    Using Transactions

    One-to-One Relationship Conventions in Entity Framework Core

    [更新]

    在您的场景中,您可以先插入 AspnetUser,然后获取 U_ID 并在 Users 表中插入一个新项目,请尝试更改代码如下:

            var result = false;
            var errorlist = new List<IdentityError>();
            var user = new ApplicationUser();
            if (ModelState.IsValid)
            {
                using (var transaction = _context.Database.BeginTransaction())
                {
                    try
                    {
                        user = new ApplicationUser { UserName = Input.Email, Email = Input.Email }; 
                        var saveresult = await _userManager.CreateAsync(user, Input.Password);
    
                        if (saveresult.Succeeded)
                        {
                            result = true;
                            //get the U_ID
                            var userid = user.U_ID;
                            //use the U_ID to create user.
                            var customeruser = new User() { U_ID = userid, PhoneNo = "12345678", Name = "Tom", Password = "Password01", Email = "tom@hotmail.com" };
                            _context.Users.Add(customeruser);
                            _context.SaveChanges();
    
                        }
                        else{
                            errorlist = saveresult.Errors.ToList();
                        }
                        transaction.Commit();
                    }
                    catch (Exception)
                    {
                        transaction.Rollback();
                    }
                } 
                if (result)
                {
                    //do something
                }
                foreach (var error in errorlist)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
            }
    

    【讨论】:

    • 你的方法也可以,但是关于身份列我认为你误解了..aspnetuser 表没有任何身份列,所以我创建的 U_ID 列实际上是 aspnetuser 中唯一的身份列表及其工作正常,如果我输入一条新记录,它可以工作,但唯一的问题是我想从 aspnetuser 表中获取这个 U_ID 值,然后使用这个 U_ID 在用户表中创建一个新记录,然后只有在两者都成功时才提交到数据库,否则回滚
    • 嗨@Jazz,请检查更新,在您的场景中,可以先插入 AspnetUser,然后获取 U_ID 并将新项目插入到用户表中。
    • 嗨,我实际上已经尝试过了,但是一旦调用 createasync 方法,它就会进入 aspnetuser 表中的记录,因此事务并没有真正发生在那里...感谢您的努力
    • 现在在我尝试了你之前告诉我的方法之后,我在这一行上得到了这个异常'Execution TImeout expired' var saveresult = await _userManager.CreateAsync(user, Input.Password);我认为它锁定了表格,但无法弄清楚为什么会出现这个异常
    • 无法重现Execution Timeout expired异常,可能如你所说,表被锁定了。尝试通过 dbcontext 创建一个新的 aspnetuser,而不是使用 UserManager.CreateAsync 方法。此外,从您的描述来看,如果先添加用户(然后添加 aspnetuser),它似乎效果很好。对于并发冲突问题,可以尝试添加Concurrency Tokens(ConcurrencyCheck or Timestamp/rowversion),参考this article
    猜你喜欢
    • 1970-01-01
    • 2017-12-12
    • 1970-01-01
    • 2013-08-18
    • 2018-09-22
    • 2022-01-01
    • 2015-05-08
    • 2014-12-07
    • 1970-01-01
    相关资源
    最近更新 更多