Jan*_* Go 3 c# sql sqlite entity-framework 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; }
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");
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的代码的示例执行...
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
问题是当我被添加到数据库时,出现“唯一约束失败:CarBrands.CarBrandId”异常。car2CarBrand honda
我期望它在第二次交易期间执行context.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...
}
Run Code Online (Sandbox Code Playgroud)
问题在于Add方法会级联:
开始跟踪给定的实体以及尚未被跟踪的其他任何可达实体,其
Added状态为在SaveChanges调用它们时将其插入数据库中。
有很多方法可以实现目标,但是最灵活的方法(我想是首选)是用Add方法替换方法调用ChangeTracker.TrackGraph:
通过遍历其导航属性开始跟踪一个实体以及所有可到达的实体。遍历是递归的,因此任何发现的实体的导航属性也将被扫描。将为每个发现的实体调用指定的回调,并且必须设置应跟踪每个实体的状态。如果未设置状态,则该实体将保持未跟踪状态。 此方法设计用于断开连接的场景,在该场景中,使用上下文的一个实例检索实体,然后使用上下文的另一个实例保存更改。一个示例是Web服务,其中一个服务调用从数据库中检索实体,而另一个服务调用保留对实体的任何更改。每个服务调用都使用一个新的上下文实例,该实例在调用完成后就被释放。如果发现上下文已经跟踪的实体,则不会处理该实体(并且不会遍历其导航属性)。
因此,context.Cars.Add(car2);您可以使用以下方法(它非常通用,并且几乎可以在所有情况下使用):
context.ChangeTracker.TrackGraph(car2, node =>
node.Entry.State = !node.Entry.IsKeySet ? EntityState.Added : EntityState.Unchanged);
Run Code Online (Sandbox Code Playgroud)
ContextEntityFramework Core 在您的代码中使用 new内部使用另一代码时出现问题。您正在混合两者之间的实体状态。只需使用相同的上下文来获取和创建即可。如果你选择:
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
}
Run Code Online (Sandbox Code Playgroud)
应该没问题。
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();
}
}
Run Code Online (Sandbox Code Playgroud)
您现在不依赖于更改跟踪机制,而是依赖于基于CarBrandId.
错误可能会产生误导,但如果您仔细查看上下文StateManager,您就会明白原因。因为CarBrand您从另一个实例获取的Context是您正在使用的实例,所以Context它的另一个实例看起来像一个新实例。您可以在调试中使用该特定数据库上下文中该特定实体的属性EntityState清楚地看到它:
它说 CarBrand 现在被添加到数据库中Id: 1,这就是 CarBrand 表的主键的唯一约束被打破。
错误表示它CarBrandId打破了限制,因为您通过由 . 支持的关系 Car-CarBrand 强制在数据库中创建新记录CarBrandId。
Id: 1在您用来查询数据库的上下文中,CarBrand 的状态是什么?当然不变。但这是唯一Context知道的人:
如果您想深入了解该主题,请Change Tracking在此处阅读 EF Core 中的概念: https: //learn.microsoft.com/en-us/ef/core/querying/tracking
| 归档时间: |
|
| 查看次数: |
1909 次 |
| 最近记录: |