Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# EF Core/Sqlite一对多关系在唯一索引约束上失败_C#_Sql_Entity Framework_Sqlite_Entity Framework Core - Fatal编程技术网

C# EF Core/Sqlite一对多关系在唯一索引约束上失败

C# EF Core/Sqlite一对多关系在唯一索引约束上失败,c#,sql,entity-framework,sqlite,entity-framework-core,C#,Sql,Entity Framework,Sqlite,Entity Framework Core,上下文是每个Car都有一个相应的CarBrand。现在我的课程如下所示: 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; } publi

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

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");
    }
}
问题是当使用相同的
CarBrand
car2
添加到数据库时,我得到'UNIQUE constraint failed:CarBrands.CarBrandId'异常

我希望它能在第二个事务的
上下文中执行。SaveChanges()
,它将添加
car2
,并适当地设置它与
CarBrand
的关系,但我得到了一个异常

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

        //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...
        }
快速修复: EntityFramework Core在使用新的
上下文时遇到问题,您的代码在使用另一个上下文时出现问题。你在两者之间混合实体的状态。
只需使用相同的上下文进行获取和创建。
如果你同意:

using (var context = new MyContext())
{
    var honda = context.CarBrands.Find(1);
    var car2 = new Car() { CarBrand = honda };
    context.Cars.Add(car2);
    context.SaveChanges(); //fine now
    var cars = context.Cars.ToList(); //will get two
}
应该没问题


如果你真的想得到两个上下文 您现在不依赖于更改跟踪机制,而是依赖于基于
CarBrandId
的普通外键关系


原因:变更跟踪 这个错误可能会产生误导,但如果您查看上下文的
StateManager
,就会发现原因。 因为您从另一个
Context
获取的
CarBrand
就是您正在使用的,对于
Context
的另一个实例,它看起来像一个新实例。 您可以在调试中清楚地看到它,在这个特定的DB上下文中,使用该特定实体的属性EntityState

它说CarBrand现在被添加到Id为
1
的数据库中,这是CarBrand表主键被破坏的唯一约束

错误表明其
CarBrandId
打破了约束,因为您通过
CarBrandId
支持的关系Car-CarBrand在数据库中强制创建了新记录

在您用来查询数据库的上下文中,Id为1的CarBrand的状态是什么当然没有改变。但这是唯一了解它的
上下文

如果您想更深入地了解该主题,请阅读EF Core中的
更改跟踪的概念:

问题在于方法层叠:

开始跟踪给定实体、以及尚未被跟踪的任何其他可访问实体,处于
添加的状态,以便在调用
保存更改时将其插入数据库

实现目标的方法有很多,但最灵活的(我想也是首选的)方法是用以下方法替换
Add
方法调用:

开始跟踪实体以及通过遍历其导航属性可访问的任何实体。遍历是递归的,因此任何发现的实体的导航属性也将被扫描。为每个发现的实体调用指定的回调,并且必须设置跟踪每个实体的状态。如果未设置状态,则实体将保持未跟踪状态。 此方法设计用于断开连接的场景,其中使用一个上下文实例检索实体,然后使用另一个上下文实例保存更改。此方法的一个示例是web服务,其中一个服务调用从数据库检索实体,另一个服务调用保留任何更改给实体。每个服务调用都使用一个新的上下文实例,该实例在调用完成时被释放。 如果发现已被上下文跟踪的实体,则不会处理该实体(并且不会遍历其导航属性)

因此,不要使用
context.Cars.Add(car2)您可以使用以下内容(这是非常通用的,几乎适用于所有场景):


不确定这是否是导致问题的原因,但我通常看不到CRUD上的静态操作。为什么每次都必须使用不同的上下文?假设我重新启动了应用程序。那么这将是在不同的上下文中,对吗?如果我真的需要在不同的上下文/事务中获取CarBrand实例,该怎么办?@paolog如果你真的想使用不同的上下文,你应该查看存储库模式和另一个添加项(我确实意识到,你可能正在尝试将比这里显示的更广泛的想法可视化)。
using (var context = new MyContext())
{
    var honda = context.CarBrands.Find(1);
    var car2 = new Car() { CarBrand = honda };
    context.Cars.Add(car2);
    context.SaveChanges(); //fine now
    var cars = context.Cars.ToList(); //will get two
}
    static void Main(string[] args)
    {
        AlwaysCreateNewDatabase();

        CarBrand hondaDb = null;
        using (var context = new MyContext())
        {
            hondaDb = context.CarBrands.Add(new CarBrand {Name = "Honda"}).Entity;
            context.SaveChanges();
        }

        using (var context = new MyContext())
        {
            var car2 = new Car() { CarBrandId = hondaDb.CarBrandId };
            context.Cars.Add(car2);
            context.SaveChanges();
        }
    }
context.ChangeTracker.TrackGraph(car2, node =>
    node.Entry.State = !node.Entry.IsKeySet ? EntityState.Added : EntityState.Unchanged);