本文介绍了EF Core/Sqlite 一对多关系在唯一索引约束上失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上下文是每个Car都有一个对应的CarBrand.现在我的课程如下所示:

公共类汽车{公共 int CarId { 获取;放;}公共 int CarBrandId { 获取;放;}公共 CarBrand CarBrand { 获取;放;}}公开课 CarBrand{公共 int CarBrandId { 获取;放;}公共字符串名称 { 获取;放;}}公共类 MyContext : DbContext{公共 DbSet汽车 { 得到;放;}public DbSet汽车品牌 { 得到;放;}protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){optionsBuilder.UseSqlite(@"Data Source = MyDatabase.sqlite");}}

这是我的代码的示例执行...

class 程序{静态无效主(字符串 [] args){AlwaysCreateNewDatabase();//第一笔交易使用 (var context = new MyContext()){var honda = new CarBrand() { Name = "Honda" };var car1 = new Car() { CarBrand = 本田};context.Cars.Add(car1);context.SaveChanges();}//第二笔交易使用 (var context = new MyContext()){var 本田 = GetCarBrand(1);var car2 = new Car() { CarBrand = 本田};context.Cars.Add(car2);context.SaveChanges();//异常发生在这里...}}静态无效 AlwaysCreateNewDatabase(){使用 (var context = new MyContext()){context.Database.EnsureDeleted();context.Database.EnsureCreated();}}静态 CarBrand GetCarBrand(int Id){使用 (var context = new MyContext()){返回 context.CarBrands.Find(Id);}}}

问题是当 car2 使用相同的 CarBrand 添加到数据库时,我得到 'UNIQUE constraint failed: CarBrands.CarBrandId' 异常> honda.

我期望它做的是在第二个交易的 context.SaveChanges() 期间,它会添加 car2 并设置它与 CarBrand 的关系适当地,但我得到了一个例外.

我真的需要在不同的上下文/事务中获取我的 CarBrand 实例.

//确实需要从不同的上下文/事务中获取 CarBrand 实例CarBrand hondaDb = null;使用 (var context = new MyContext()){本田Db = context.CarBrands.First(x => x.Name == "Honda");}//第二笔交易使用 (var context = new MyContext()){var car2 = new Car() { CarBrand = hondaDb };context.Cars.Add(car2);context.SaveChanges();//异常发生在这里...}
解决方案

问题在于 Add 方法级联:

开始跟踪给定实体,以及任何其他尚未被跟踪的可访问实体,处于 Added 状态,以便在 Added 状态下将它们插入到数据库中code>SaveChanges 被调用.

有很多方法可以实现目标,但最灵活的(我猜也是首选)是将 Add 方法调用替换为 ChangeTracker.TrackGraph:code>开始跟踪实体以及通过遍历其导航属性可访问的任何实体.遍历是递归的,因此任何发现的实体的导航属性也将被扫描.为每个发现的实体调用指定的回调,并且必须设置应跟踪每个实体的状态.如果未设置状态,则实体保持未跟踪状态.此方法设计用于断开连接的场景,其中使用上下文的一个实例检索实体,然后使用上下文的不同实例保存更改.此示例是一个 Web 服务,其中一个服务调用从数据库中检索实体,另一个服务调用保留对实体的任何更改.每个服务调用都使用一个新的上下文实例,该实例在调用完成时被释放.如果发现一个实体已被上下文跟踪,则不会处理该实体(并且不会遍历其导航属性).

因此,您可以使用以下内容代替 context.Cars.Add(car2);(它非常通用,几乎适用于所有场景):

context.ChangeTracker.TrackGraph(car2, node =>node.Entry.State = !node.Entry.IsKeySet ?EntityState.Added : EntityState.Unchanged);

The context is each Car has a corresponding CarBrand. Now my classes are as shown below:

public class Car
{
    public int CarId { get; set; }
    public int CarBrandId { get; set; }
    public CarBrand CarBrand { get; set; }
}

public class CarBrand
{
    public int CarBrandId { get; set; }
    public string Name { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<Car> Cars { get; set; }
    public DbSet<CarBrand> CarBrands { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite(@"Data Source = MyDatabase.sqlite");
    }
}

Here's a sample execution of my code...

class Program
{
    static void Main(string[] args)
    {
        AlwaysCreateNewDatabase();

        //1st transaction
        using (var context = new MyContext())
        {
            var honda = new CarBrand() { Name = "Honda" };
            var car1 = new Car() { CarBrand = honda };
            context.Cars.Add(car1);
            context.SaveChanges();
        }

        //2nd transaction
        using (var context = new MyContext())
        {
            var honda = GetCarBrand(1);
            var car2 = new Car() { CarBrand = honda };
            context.Cars.Add(car2);
            context.SaveChanges(); // exception happens here...
        }
    }

    static void AlwaysCreateNewDatabase()
    {
        using (var context = new MyContext())
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();
        }
    }

    static CarBrand GetCarBrand(int Id)
    {
        using (var context = new MyContext())
        {
            return context.CarBrands.Find(Id);
        }
    }
}

The problem is I get 'UNIQUE constraint failed: CarBrands.CarBrandId' exception when car2 is being added to the database with the same CarBrand honda.

What I expect it to do is during 2nd transaction's context.SaveChanges(), it will add car2 and set it's relationship with CarBrand appropriately but I get an exception instead.

EDIT: I really need to get my CarBrand instance in a different context/transaction.

        //really need to get CarBrand instance from different context/transaction
        CarBrand hondaDb = null;
        using (var context = new MyContext())
        {
            hondaDb = context.CarBrands.First(x => x.Name == "Honda");
        }

        //2nd transaction
        using (var context = new MyContext())
        {
            var car2 = new Car() { CarBrand = hondaDb };
            context.Cars.Add(car2);
            context.SaveChanges(); // exception happens here...
        }
解决方案

The problem is that Add method cascades:

There are many ways to achieve the goal, but the most flexible (and I guess the preferred) is to replace the Add method call with the ChangeTracker.TrackGraph method:

So instead of context.Cars.Add(car2); you could use the following (it's pretty generic and should work in almost all scenarios):

context.ChangeTracker.TrackGraph(car2, node =>
    node.Entry.State = !node.Entry.IsKeySet ? EntityState.Added : EntityState.Unchanged);

这篇关于EF Core/Sqlite 一对多关系在唯一索引约束上失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-11 16:51