【问题标题】:Cannot persist relations in Core Identity's ApplicationUser无法在核心身份的 ApplicationUser 中保留关系
【发布时间】:2020-03-21 23:43:13
【问题描述】:

我找不到正确扩展 ASP.NET Core 的 IdentityUser 的方法(扩展后:ApplicationUser)。我可以覆盖它并将其与其他模型链接,但不能从用户链接到其他模型。像CustomTag 这样的简单数据有效。

问题源代码:https://github.com/chanibal/CoreIdentityIssue
或者,具体来说,it's second commit that contains all the changes over the default template

我做了什么:

  1. 已创建新项目:
    ASP.NET Core Web 应用程序(模型-视图-控制器)
    身份验证:个人用户帐户,在应用程序中存储用户帐户
    更新了数据库
  2. 更改了覆盖IdentityUser 的模型(如official docs):

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
            {}
    
        public DbSet<Bar> Bars { get; set; }
        public DbSet<Foo> Foos { get; set; }
        public DbSet<ApplicationUser> ApplicationUsers { get; set; } //< doesn't help
    }
    
    public class ApplicationUser : IdentityUser
    {
        public string CustomTag { get; set; }   //< this works
        public Bar Bar { get; set; }            //< THIS DOES NOT WORK
    }
    
    public class Bar
    {
        public int Id { get; set; }
        public int Value { get; set; }
    }
    
    public class Foo
    {
        public int Id { get; set; }
        public ApplicationUser User { get; set; }   //< this works
    }
    

    并迁移了更改

  3. 我正在以这种方式更新 Bar 值:

    /// This should ensure a Bar is connected to a ApplicationUser
    /// and increment it's value
    [Authorize]
    public async Task<IActionResult> IncrementBar()
    {
        // This DOES NOT work
        var user = await _userManager.GetUserAsync(HttpContext.User);
        if (user.Bar == null)   //< this is always null
        {
            user.Bar = new Bar() { Value = 0 };
            // _context.Add(user.Bar); //< doesn't help
        }
        user.Bar.Value++;
        // await _userManager.UpdateAsync(user); //<> doesn't help
        // await _signInManager.RefreshSignInAsync(user);  //< doesn't help, starting to get desperate
        await _context.SaveChangesAsync();

        return RedirectToAction(nameof(Index));
    }

信息在数据库中,可通过 SQL 访问。 但不知何故,它并没有为ApplicationUser 模型提供水分:

使用 Visual Studio 16.3.9

【问题讨论】:

  • 添加了该问题的最小演示
  • 外键在生成的数据库中是什么样子的?
  • 外键配置可从model snapshot 获得。但请注意,这个问题已经得到解答。感谢您的帮助。

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


【解决方案1】:

EF 不会自动加载相关实体。您必须急切地或显式地加载关系。急切加载是首选方式,因为它会在单个查询中获取所有数据。但是,UserManager&lt;TUser&gt; 无法提供急切加载关系的方法。因此,您有两种选择:

  1. 显式加载关系。不过,这将需要一个额外的查询。

    var user = await _userManager.GetUserAsync(HttpContext.User);
    await _context.Entry(user).Reference(x => x.Bar).LoadAsync();
    // note: for collection props, you'd use `Collection(x => x.CollectionProp).LoadAsync()` instead.
    
  2. 通过用户ID从上下文中查询用户,而不是使用UserManager&lt;TUser&gt;

    var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
    var user = await _context.Users.Include(x => x.Bar).SingleOrDefaultAsync(x => x.Id == userId); 
    

【讨论】:

  • 谢谢,您的两个解决方案都有效。我被误导了,EF 确实会急切地加载——因为如果它有可用数据,它会链接它,尽管没有在 SQL 级别加入,例如,通过UserManager 加载用户然后选择_context.Foos.ToArray()FooApplicationUser 属性),每个 Foo 确实有一个 User 实例 - 因为它之前已加载并且在某种缓存中。
  • 你没有被误导。你误会了。 EF 确实有一个对象缓存,当它在对象缓存中有这些关系的数据时,它会自动修复关系。不过,这部分很关键。它必须已经存在。如果您稍后查询某些内容,EF 将不会返回并更改您已经退出的实体。此外,上下文的范围仅限于请求,因此您可能已经在之前的请求中查询过某些内容,但该内容已不存在
  • 是的。再次感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-28
  • 2022-10-12
  • 1970-01-01
  • 2011-04-16
  • 1970-01-01
  • 1970-01-01
  • 2020-04-16
相关资源
最近更新 更多