"The instance of entity type cannot be tracked" 添加分离的实体树时出错
"The instance of entity type cannot be tracked" error when adding detached entity tree
我无法添加交易实体,因为买方和卖方引用的是同一个城市“City1”。
我在这里使用分离的实体,因为我通过 API.
接收整个模型
// This is the result of Deal entity deserialization
var deal = new Deal
{
Id = "D1",
Buyer = new Person
{
Id = "P1",
Name = "Person1",
City = new City { Name = "City1" }
},
Seller = new Person
{
Id = "P2",
Name = "Person2",
City = new City { Name = "City1" }
}
};
_dbContext.Deals.Add(deal);
_dbContext.SaveChanges();
错误是
System.InvalidOperationException: 'The instance of entity type 'City' cannot be tracked because another instance with the same key value for {'Name'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
以下是实体的设置方式:
modelBuilder.Entity<City>(x =>
{
x.HasKey(e => e.Name);
});
modelBuilder.Entity<Person>(x =>
{
x.HasKey(e => e.Id);
x.Property(e => e.Name);
x.HasOne(e => e.City).WithMany();
});
modelBuilder.Entity<Deal>(x =>
{
x.HasKey(e => e.Id);
x.HasOne(e => e.Buyer).WithMany();
x.HasOne(e => e.Seller).WithMany();
});
与此类实体合作的正确方法是什么?
我使用 EF Core 6
您的两个 Person
对象需要为 City
引用同一个对象。或者,公开并使用 FK 属性(这似乎是城市的 Name
属性)而不是此操作的导航 属性。
如果您想继续使用 JSON 中的 City
对象,则需要保留引用。例如,这里是 System.Text.Json
的文档
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace PreserveReferences
{
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public List<Employee> DirectReports { get; set; }
}
public class Program
{
public static void Main()
{
Employee tyler = new()
{
Name = "Tyler Stein"
};
Employee adrian = new()
{
Name = "Adrian King"
};
tyler.DirectReports = new List<Employee> { adrian };
adrian.Manager = tyler;
JsonSerializerOptions options = new()
{
ReferenceHandler = ReferenceHandler.Preserve,
WriteIndented = true
};
string tylerJson = JsonSerializer.Serialize(tyler, options);
Console.WriteLine($"Tyler serialized:\n{tylerJson}");
Employee tylerDeserialized =
JsonSerializer.Deserialize<Employee>(tylerJson, options);
Console.WriteLine(
"Tyler is manager of Tyler's first direct report: ");
Console.WriteLine(
tylerDeserialized.DirectReports[0].Manager == tylerDeserialized);
}
}
}
// Produces output like the following example:
//
//Tyler serialized:
//{
// "$id": "1",
// "Name": "Tyler Stein",
// "Manager": null,
// "DirectReports": {
// "$id": "2",
// "$values": [
// {
// "$id": "3",
// "Name": "Adrian King",
// "Manager": {
// "$ref": "1"
// },
// "DirectReports": null
// }
// ]
// }
//}
//Tyler is manager of Tyler's first direct report:
//True
如果稍微修改一下代码,就可以避免跟踪具有相同键的两个对象:
var newCity = new City { Name = "City1" };
_dbContext.Cities.Add(newCity);
var p1 = new Person
{
Id = "P1",
Name = "Person1",
City = newCity
};
_dbContext.Persons.Add(p1);
var p2 = new Person
{
Id = "P2",
Name = "Person2",
City = newCity
};
_dbContext.Persons.Add(p2);
var deal = new Deal
{
Id = "D1",
Buyer = p1,
Seller = p2
};
_dbContext.Deals.Add(deal);
_dbContext.SaveChanges();
这样,您就告诉 Entity framework 这是同一个城市 - 您只是引用该对象并将该对象添加到城市列表中。保存将根据您的 table 定义以正确的顺序保留它。
同样,如果您仍然 运行 遇到问题,因为您添加了同一个人 - 首先创建人员对象并将其添加到人员列表中。
还有一件事 - 如果某个对象可能已经存在,请查询它。
例如:
var cityName = "City3";
var city = _dbContext.Cities.FirstOrDefault(f => f.Name == cityName);
if (city == null)
{
city = new City { Name = cityName };
_dbContext.Cities.Add(city);
}
// from here on you can safely use city
我无法添加交易实体,因为买方和卖方引用的是同一个城市“City1”。 我在这里使用分离的实体,因为我通过 API.
接收整个模型// This is the result of Deal entity deserialization
var deal = new Deal
{
Id = "D1",
Buyer = new Person
{
Id = "P1",
Name = "Person1",
City = new City { Name = "City1" }
},
Seller = new Person
{
Id = "P2",
Name = "Person2",
City = new City { Name = "City1" }
}
};
_dbContext.Deals.Add(deal);
_dbContext.SaveChanges();
错误是
System.InvalidOperationException: 'The instance of entity type 'City' cannot be tracked because another instance with the same key value for {'Name'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
以下是实体的设置方式:
modelBuilder.Entity<City>(x =>
{
x.HasKey(e => e.Name);
});
modelBuilder.Entity<Person>(x =>
{
x.HasKey(e => e.Id);
x.Property(e => e.Name);
x.HasOne(e => e.City).WithMany();
});
modelBuilder.Entity<Deal>(x =>
{
x.HasKey(e => e.Id);
x.HasOne(e => e.Buyer).WithMany();
x.HasOne(e => e.Seller).WithMany();
});
与此类实体合作的正确方法是什么? 我使用 EF Core 6
您的两个 Person
对象需要为 City
引用同一个对象。或者,公开并使用 FK 属性(这似乎是城市的 Name
属性)而不是此操作的导航 属性。
如果您想继续使用 JSON 中的 City
对象,则需要保留引用。例如,这里是 System.Text.Json
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace PreserveReferences
{
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public List<Employee> DirectReports { get; set; }
}
public class Program
{
public static void Main()
{
Employee tyler = new()
{
Name = "Tyler Stein"
};
Employee adrian = new()
{
Name = "Adrian King"
};
tyler.DirectReports = new List<Employee> { adrian };
adrian.Manager = tyler;
JsonSerializerOptions options = new()
{
ReferenceHandler = ReferenceHandler.Preserve,
WriteIndented = true
};
string tylerJson = JsonSerializer.Serialize(tyler, options);
Console.WriteLine($"Tyler serialized:\n{tylerJson}");
Employee tylerDeserialized =
JsonSerializer.Deserialize<Employee>(tylerJson, options);
Console.WriteLine(
"Tyler is manager of Tyler's first direct report: ");
Console.WriteLine(
tylerDeserialized.DirectReports[0].Manager == tylerDeserialized);
}
}
}
// Produces output like the following example:
//
//Tyler serialized:
//{
// "$id": "1",
// "Name": "Tyler Stein",
// "Manager": null,
// "DirectReports": {
// "$id": "2",
// "$values": [
// {
// "$id": "3",
// "Name": "Adrian King",
// "Manager": {
// "$ref": "1"
// },
// "DirectReports": null
// }
// ]
// }
//}
//Tyler is manager of Tyler's first direct report:
//True
如果稍微修改一下代码,就可以避免跟踪具有相同键的两个对象:
var newCity = new City { Name = "City1" };
_dbContext.Cities.Add(newCity);
var p1 = new Person
{
Id = "P1",
Name = "Person1",
City = newCity
};
_dbContext.Persons.Add(p1);
var p2 = new Person
{
Id = "P2",
Name = "Person2",
City = newCity
};
_dbContext.Persons.Add(p2);
var deal = new Deal
{
Id = "D1",
Buyer = p1,
Seller = p2
};
_dbContext.Deals.Add(deal);
_dbContext.SaveChanges();
这样,您就告诉 Entity framework 这是同一个城市 - 您只是引用该对象并将该对象添加到城市列表中。保存将根据您的 table 定义以正确的顺序保留它。
同样,如果您仍然 运行 遇到问题,因为您添加了同一个人 - 首先创建人员对象并将其添加到人员列表中。
还有一件事 - 如果某个对象可能已经存在,请查询它。 例如:
var cityName = "City3";
var city = _dbContext.Cities.FirstOrDefault(f => f.Name == cityName);
if (city == null)
{
city = new City { Name = cityName };
_dbContext.Cities.Add(city);
}
// from here on you can safely use city