实体框架核心导航未更新

时间:2021-03-11 22:00:00

标签: entity-framework-core

我为此使用了 Entity Framework Core v5.0.3。

我正在为用户制作一个简单的信息应用程序,其中每个用户都可以拥有自己喜欢的颜色。

人物模型

public class Person 
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int PersonId { get; set; }
    [MaxLength(50)]
    public string FirstName { get; set; }
    [MaxLength(50)]
    public string LastName { get; set; }
    public bool IsAuthorised { get; set; }
    public bool IsValid { get; set; }
    public bool IsEnabled { get; set; }
    public virtual ICollection<FavouriteColour> FavouriteColours { get; set; }
}

最喜欢的颜色模型

public class FavouriteColour
{
    [Key]
    public int PersonId { get; set; }
    public virtual Person Person { get; set; }
    [Key]
    public int ColourId { get; set; }
    public virtual Colour Colour { get; set; }
}

颜色模型

public class Colour
{
    [Key]
    public int ColourId { get; set; }
    public string Name { get; set; }
    public bool IsEnabled { get; set; }
    public virtual IList<FavouriteColour> FavouriteColours { get; set; }
}

在我的数据库上下文中,我已将它们定义为这样

protected override void OnModelCreating(ModelBuilder modelBuilder) 
{
        modelBuilder.Entity<FavouriteColour>()
            .HasKey(c => new { c.PersonId, c.ColourId });

        modelBuilder.Entity<FavouriteColour>()
            .HasOne(fc => fc.Person)
            .WithMany(p => p.FavouriteColours)
            .HasForeignKey(p => p.PersonId);

        modelBuilder.Entity<FavouriteColour>()
            .HasOne(fc => fc.Colour)
            .WithMany(c => c.FavouriteColours)
            .HasForeignKey(c => c.ColourId);
}

现在在应用程序中,用户可以添加或删除喜欢的颜色,以便在调用存储库更新的控制器中接收新的用户对象

public async Task<IActionResult> PutPerson(int id, Person person) 
{
    peopleRepository.Update(person);
}

然后存储库进行更新

public void Update(Person person) 
{
    _context.Update(person);
}

执行 await _context.SaveChangesAsync(); 后,颜色没有任何变化。我认为所有这些模型的目的都是为了颜色会自动改变?

我设置了一个 FavouriteColourRepository 来进行这样的更新

public async Task<bool> Update(int personId, ICollection<FavouriteColour> favouriteColours) 
{
    // empty their favourite colours
    _context.FavouriteColours.RemoveRange(_context.FavouriteColours.Where(fc => fc.PersonId == personId);

    // add new favourite colours
    _context.FavouriteColours.AddRange(favouriteColours);

    return true;
}

我把我的控制器改成了这个

public async Task<IActionResult> PutPerson(int id, Person person)
{
    peopleRepository.Update(person);
    bool valid = await favouriteColourRepository.Update(id, person.FavouriteColours);
}

但由于某种原因我无法弄清楚

_context.FavouriteColours.RemoveRange(_context.FavouriteColours.Where(fc => fc.PersonId == personId);

实际上更改了 favouriteColours 参数并将最后一个状态强制到其中创建重复的主键并且插入失败。

那么为什么新的最喜欢的颜色永远不会被插入,为什么当我试图清除用户已有的所有颜色时我的 favouriteColours 参数被编辑?

1 个答案:

答案 0 :(得分:1)

<块引用>

为什么永远不会插入新的最喜欢的颜色

调用 _context.Update(person);Person 实体和所有相关的 FavouriteColour 实体置于 Modified 状态。因此,在下一次 SaveChanges 调用中,EF 应该向数据库提交更新命令。

如果您修改 Person 实体的​​任何属性,您会发现此人已正确更新。 FavouriteColour 实体的​​问题在于它只包含主键属性,并且 EF 不修改键属性(或作为复合主键一部分的任何属性)。您可以使用以下代码进行测试 -

var fc = dbCtx.FavouriteColours
        .FirstOrDefault(p => p.PersonId == 1 && p.ColourId == 4);
                    
fc.ColourId = 6;    // CoulourId is part of primary key
dbCtx.SaveChanges();

你会遇到一个例外 -

<块引用>

属性 'FavouriteColour.ColourId' 是键的一部分,因此不能 被修改或标记为已修改。 ...

因此,EF 甚至不会为 FavouriteColours 生成任何更新命令,即使它们都被标记为 Modified

要更新 Person 及其所有 FavouriteColour,您需要执行类似 -

// fetch the exising person with all its FavouriteColours
var existingPerson = dbCtx.Persons
                    .Include(p => p.FavouriteColours)
                    .FirstOrDefault(p => p.Id == id);
                    
// modify any properties of Person if you need

// replace the existing FavouriteColours list
existingPerson.FavouriteColours = person.FavouriteColours;

// save the changes
dbCtx.SaveChanges();

这将 -

  1. 删除现有列表中但不在新列表中的任何 FavouriteColour
  2. 插入新列表中但不在现有列表中的任何 FavouriteColour

在您的存储库就位后,您如何实现这一点取决于您。

<块引用>

为什么我的 favouriteColours 参数在我尝试时被编辑 清除用户已有的所有颜色

在此操作之前,您调用了 _context.Update(person);。因此,Person 现在作为现有实体在 Modified 状态下被跟踪。然后当你打电话 -

_context.FavouriteColours.RemoveRange(_context.FavouriteColours.Where(fc => fc.PersonId == personId));

_context.FavouriteColours.Where(fc => fc.PersonId == personId) 部分,从数据库中获取该 FavouriteColours 的现有 Person。因此,这些提取的 FavouriteColours 会添加到此人的 FavouriteColours 列表中(因为它正在被跟踪)。

例如,假设在您的控制器中,您收到了一个 Person 实体,其中包含 4 个 FavouriteColour,而数据库中有 3 个 FavouriteColour 用于此人。调用 _context.Update(person); 后,将使用 4 FavouriteColour 的列表跟踪此人。然后,当您从数据库中获取此人现有的 FavouriteColours 时,正在跟踪此人,其中包含总共 7 个 FavouriteColour 的列表。

我希望能遮蔽任何光线。

相关问题