将反序列化的 JSON 个对象保存到具有重复子实体的数据库
Saving deserialized JSON objects to database with duplicate child entities
我正在从 API 调用中检索一些 JSON 并将其反序列化为它的组件对象。一切正常,直到我开始保存到数据库。原因是,存在具有重复键的子对象(就数据而言,这是绝对正确的)但是当我保存顶级对象时,它会在子对象上引发主键冲突错误。
这是我的示例 JSON(我知道它不完整);
{
"count": 149,
"filters": {},
"competitions": [
{
"id": 2006,
"area": {
"id": 2001,
"name": "Africa",
"countryCode": "AFR",
"ensignUrl": null
},
"name": "WC Qualification",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 555,
"startDate": "2019-09-04",
"endDate": "2021-11-16",
"currentMatchday": null,
"winner": null
},
"numberOfAvailableSeasons": 2,
"lastUpdated": "2018-06-04T23:54:04Z"
},
{
"id": 2025,
"area": {
"id": 2011,
"name": "Argentina",
"countryCode": "ARG",
"ensignUrl": null
},
"name": "Supercopa Argentina",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 430,
"startDate": "2019-04-04",
"endDate": "2019-04-04",
"currentMatchday": null,
"winner": null
},
"numberOfAvailableSeasons": 2,
"lastUpdated": "2019-05-03T05:08:18Z"
},
{
"id": 2023,
"area": {
"id": 2011,
"name": "Argentina",
"countryCode": "ARG",
"ensignUrl": null
},
"name": "Primera B Nacional",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 547,
"startDate": "2019-08-16",
"endDate": "2020-06-14",
"currentMatchday": 30,
"winner": null
},
"numberOfAvailableSeasons": 3,
"lastUpdated": "2020-05-15T00:00:02Z"
},
目前我只保存顶级对象,我 expect/want 也保存所有子对象。如果我关闭子对象上的主键(使它们成为标识列而不是它们的实际值),这一切都可以正常工作并且所有子对象都可以完美保存。正如您从 JSON 中看到的,"area" 2011 是重复的,有两个具有相同区域的比赛,所以数据明智它是正确的,但是 [=27 的正确主键=] 打开,它在尝试插入重复记录时跳闸。
所以我完全理解发生了什么以及为什么会出错,我想知道的是,是否有一种简单的方法可以告诉 EF 忽略重复键错误。我无法添加 try catch 来保存顶级对象,因为它在遇到错误时什么也没有保存。
我试过保存单个子对象,在保存之前测试它们是否存在,但是当它试图保存父级对象时,它也试图保存子对象,给我留下了同样的问题。
这是我保存顶级对象的代码(为简单起见被删减);
public class Area
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string name { get; set; }
public string countryCode { get; set; }
public string ensignUrl { get; set; }
}
public class Winner
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string name { get; set; }
public string shortName { get; set; }
public string tla { get; set; }
public string crestUrl { get; set; }
}
public class CurrentSeason
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string startDate { get; set; }
public string endDate { get; set; }
public int? currentMatchday { get; set; }
public Winner winner { get; set; }
}
public class Competition
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public Area area { get; set; }
public string name { get; set; }
public string code { get; set; }
public string emblemUrl { get; set; }
public string plan { get; set; }
public CurrentSeason currentSeason { get; set; }
public int numberOfAvailableSeasons { get; set; }
public DateTime lastUpdated { get; set; }
}
public class Example
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int count { get; set; }
public IList<Competition> competitions { get; set; }
}
static void Main(string[] args)
{
string json = GET(@"http://my.url.com/api/stuff");
Example example = JsonConvert.DeserializeObject<Example>(json);
using(var db = new ExampleContext())
{
db.Examples.Add(example);
db.SaveChanges();
}
}
感谢期待。
很遗憾,没有任何直接的方法可以解决您的问题。
EF Change Tracker
通过引用跟踪实体,解决问题的唯一方法是为所有相同的 areas
.
创建相同的对象
为此你有两个选择:
1- 在这一行
之后循环 example
Example example = JsonConvert.DeserializeObject<Example>(json);
并找到所有相同的 areas
并将所有替换为其中之一。
2- 使用 NewtonSoft 的 PreserveReferencesHandling 功能。但它需要同时应用于序列化和反序列化:
服务器(Api)端:
string json = JsonConvert.SerializeObject(data, Formatting.Indented,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
客户端:
var example = JsonConvert.DeserializeObject<Example>(json,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
我正在从 API 调用中检索一些 JSON 并将其反序列化为它的组件对象。一切正常,直到我开始保存到数据库。原因是,存在具有重复键的子对象(就数据而言,这是绝对正确的)但是当我保存顶级对象时,它会在子对象上引发主键冲突错误。
这是我的示例 JSON(我知道它不完整);
{
"count": 149,
"filters": {},
"competitions": [
{
"id": 2006,
"area": {
"id": 2001,
"name": "Africa",
"countryCode": "AFR",
"ensignUrl": null
},
"name": "WC Qualification",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 555,
"startDate": "2019-09-04",
"endDate": "2021-11-16",
"currentMatchday": null,
"winner": null
},
"numberOfAvailableSeasons": 2,
"lastUpdated": "2018-06-04T23:54:04Z"
},
{
"id": 2025,
"area": {
"id": 2011,
"name": "Argentina",
"countryCode": "ARG",
"ensignUrl": null
},
"name": "Supercopa Argentina",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 430,
"startDate": "2019-04-04",
"endDate": "2019-04-04",
"currentMatchday": null,
"winner": null
},
"numberOfAvailableSeasons": 2,
"lastUpdated": "2019-05-03T05:08:18Z"
},
{
"id": 2023,
"area": {
"id": 2011,
"name": "Argentina",
"countryCode": "ARG",
"ensignUrl": null
},
"name": "Primera B Nacional",
"code": null,
"emblemUrl": null,
"plan": "TIER_FOUR",
"currentSeason": {
"id": 547,
"startDate": "2019-08-16",
"endDate": "2020-06-14",
"currentMatchday": 30,
"winner": null
},
"numberOfAvailableSeasons": 3,
"lastUpdated": "2020-05-15T00:00:02Z"
},
目前我只保存顶级对象,我 expect/want 也保存所有子对象。如果我关闭子对象上的主键(使它们成为标识列而不是它们的实际值),这一切都可以正常工作并且所有子对象都可以完美保存。正如您从 JSON 中看到的,"area" 2011 是重复的,有两个具有相同区域的比赛,所以数据明智它是正确的,但是 [=27 的正确主键=] 打开,它在尝试插入重复记录时跳闸。
所以我完全理解发生了什么以及为什么会出错,我想知道的是,是否有一种简单的方法可以告诉 EF 忽略重复键错误。我无法添加 try catch 来保存顶级对象,因为它在遇到错误时什么也没有保存。
我试过保存单个子对象,在保存之前测试它们是否存在,但是当它试图保存父级对象时,它也试图保存子对象,给我留下了同样的问题。
这是我保存顶级对象的代码(为简单起见被删减);
public class Area
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string name { get; set; }
public string countryCode { get; set; }
public string ensignUrl { get; set; }
}
public class Winner
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string name { get; set; }
public string shortName { get; set; }
public string tla { get; set; }
public string crestUrl { get; set; }
}
public class CurrentSeason
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public string startDate { get; set; }
public string endDate { get; set; }
public int? currentMatchday { get; set; }
public Winner winner { get; set; }
}
public class Competition
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int id { get; set; }
public Area area { get; set; }
public string name { get; set; }
public string code { get; set; }
public string emblemUrl { get; set; }
public string plan { get; set; }
public CurrentSeason currentSeason { get; set; }
public int numberOfAvailableSeasons { get; set; }
public DateTime lastUpdated { get; set; }
}
public class Example
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int count { get; set; }
public IList<Competition> competitions { get; set; }
}
static void Main(string[] args)
{
string json = GET(@"http://my.url.com/api/stuff");
Example example = JsonConvert.DeserializeObject<Example>(json);
using(var db = new ExampleContext())
{
db.Examples.Add(example);
db.SaveChanges();
}
}
感谢期待。
很遗憾,没有任何直接的方法可以解决您的问题。
EF Change Tracker
通过引用跟踪实体,解决问题的唯一方法是为所有相同的 areas
.
为此你有两个选择:
1- 在这一行
之后循环example
Example example = JsonConvert.DeserializeObject<Example>(json);
并找到所有相同的 areas
并将所有替换为其中之一。
2- 使用 NewtonSoft 的 PreserveReferencesHandling 功能。但它需要同时应用于序列化和反序列化:
服务器(Api)端:
string json = JsonConvert.SerializeObject(data, Formatting.Indented,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
客户端:
var example = JsonConvert.DeserializeObject<Example>(json,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });