【问题标题】:Can't update IdentityUser - double SQL update无法更新 IdentityUser - 双重 SQL 更新
【发布时间】:2022-07-04 19:53:41
【问题描述】:

我有一个带有 Mvc REST Api 的 Angular 网站。我刚刚将它从 .net core 2.0 更新到最新的 .Net 6,以及 EF Core 和 AspNetCore.Identity。

我有一个扩展的 AspNetCore 的 IdentityUser。当我尝试更新它时,发送了 2 个更新请求(我使用 Sql Server Profiler 发现了) - 一个包含更新的列,另一个将其重置为原始值。它只发生在 IdentityUser 上,其他实体正常工作。结果,我无法更新任何用户。

它们有时会以不同的顺序出现,因此有时更新确实有效(但更多时候无效)。

例如当我尝试这样的事情时

        var user = await UserAccountManager.UserManager.FindByIdAsync(id);
        user.Name = model.Name;
        var result = await UserAccountManager.UserManager.UpdateAsync(user);

在此之后,我会在探查器中看到类似这样的内容:

如您所见,有 2 个后续更新,它们的名称字段和 ConcurrencyStamp 不同。

我尝试直接从上下文中获取用户,例如:

                var xx = await Context.Users.SingleOrDefaultAsync(m => m.Id == id);
                xx.Name = model.Name;
                var aa = await Context.SaveChangesAsync();

同样的事情。

即使将其全部包装在事务中也不起作用 - SQL 请求被分离出来,但在更新用户之后,仍然发送了另一个 SQL 查询将其还原。

如果有帮助,我将粘贴到 ConfigureServices 函数中(来自 Startup.cs):

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().AddNewtonsoftJson(options => {
                                             options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                                             options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                                         });

        services.AddDbContext<OffWorkDbContext>(options =>
                                                    {
                                                        options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"], b => b.MigrationsAssembly("Hicron.OffWorkPlanner.DataAccessComponent"));
                                                    });

        // add identity
        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<OffWorkDbContext>()
            .AddDefaultTokenProviders();

        services.Configure<IdentityOptions>(o => 
                                            {
                                                // User settings
                                                o.User.RequireUniqueEmail = true;
                                                o.Password.RequireDigit = false;
                                                o.Password.RequireNonAlphanumeric = false;
                                                o.Password.RequireUppercase = false;
                                                o.Password.RequireLowercase = false;
                                                //o.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
                                                //o.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
                                                //o.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
                                            });

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
                          {
                              options.TokenValidationParameters = new TokenValidationParameters
                                                                  {
                                                                      ValidateIssuer = true,
                                                                      ValidateAudience = true,
                                                                      ValidateLifetime = true,
                                                                      ValidateIssuerSigningKey = true,
                                                                      ValidIssuer = Configuration["Token:Issuer"],
                                                                      ValidAudience = Configuration["Token:Issuer"],
                                                                      IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Token:Key"]))
                                                                  };
                          });

        services.AddAuthorization(options =>
                                  {
                                      //add authorization policies 
                                      options.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
                                          .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
                                          .RequireAuthenticatedUser().Build());
                                  });

        Mapper.Initialize(cfg =>
                          {
                              cfg.AddProfile<AutoMapperProfile>();
                          });


        // Add cors
        services.AddCors();

        // Add framework services.
        services.AddMvc(options =>
        {
            options.EnableEndpointRouting = false;
        });


        // In production, the Angular files will be served from this directory
        services.AddSpaStaticFiles(configuration =>
                                   {
                                       configuration.RootPath = "ClientApp/dist";
                                   });

        services.Configure<EmailConfig>(Configuration.GetSection("SmtpConfig"));
        services.AddScoped<IEmailNotifications, EmailNotifications>();
        services.AddScoped<IUserAccountManager, UserAccountManager>();
        services.AddScoped<ITeamService, TeamService>();
        services.AddScoped<IUserService, UserService>();
        services.AddScoped<IDayService, DayService>();
        services.AddScoped<IProjectService, ProjectService>();
        services.AddScoped<IUserCalendarItemService, UserCalendarItemService>();
        services.AddScoped<IDepartmentService, DepartmentService>();
        services.AddTransient<IDatabaseInitializer, DatabaseInitializer>();
    }

请帮我弄清楚这里发生了什么(并更新用户)。

【问题讨论】:

  • 您收到的异常/消息是什么?
  • 偶尔我会收到 DbUpdateConcurrencyException(如果您有 2 个更新混在一起,这是有道理的),但总的来说我没有收到。

标签: .net asp.net-mvc entity-framework-core asp.net-identity


【解决方案1】:

好的,原来有 2 个并发请求 - 第 2 个是更新角色,即使角色没有明确更新用户,它确实在幕后更改了并发时间戳,出于某种原因它是用过时的值来做。

为了解决这个问题,我们只是将这两个请求放在一起。

【讨论】:

    猜你喜欢
    • 2017-04-24
    • 2012-03-23
    • 1970-01-01
    • 2013-04-29
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    相关资源
    最近更新 更多