我有一个 Language
模型定义如下:
public class Language
{
[JsonProperty("iso_639_1")]
public string Iso { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
public override bool Equals(object obj)
{
if (!(obj is Language))
{
return false;
}
return ((Language)obj).Iso == Iso;
}
public override int GetHashCode()
{
return Iso.GetHashCode();
}
}
这在模型 Movie
中用作 ICollection<Language> SpokenLanguages
. 我正在使用我收集的信息播种我的数据库 . 当多部电影使用相同的语言时,我显然想重新使用 Languages
表中的现有条目 .
以下通过重用现有类型并添加新类型来实现:
var localLanguages = context.Languages.ToList();
var existingLanguages = localLanguages.Union(movie.SpokenLanguages);
var newLanguages = localLanguages.Except(existingLanguages).ToList();
newLanguages.AddRange(existingLanguages);
movie.SpokenLanguages = newLanguages;
这有效,但显然这是相当丑陋,而不是EF友好 . 我正在考虑将现有模型附加到EF并让它自动重用它但我似乎无法让它工作 - 我最终得到这个错误消息:
附加“Models.Movies.Language”类型的实体失败,因为同一类型的另一个实体已具有相同的主键值 . 如果图中的任何实体具有冲突的键值,则在使用“附加”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况 . 这可能是因为某些实体是新的并且尚未收到数据库生成的键值 . 在这种情况下,使用“添加”方法或“已添加”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“已修改” .
有问题的代码是这样的:
var localLanguages = context.Languages.ToList();
foreach (var language in movie.SpokenLanguages)
{
if (localLanguages.Contains(language))
{
context.Languages.Attach(language);
// no difference between both approaches
context.Entry(language).State = EntityState.Unchanged;
}
}
将状态设置为 Unchanged
或 Modified
没有任何区别 . 我收到的JSON响应是
{
"iso_639_1": "en",
"name": "English"
}
这两个字段与数据库中存在的值完全相同 .
数据库中的每个插入都会创建一个新的上下文并对其进行处理 .
如何让EF重新使用现有的语言条目而不必自己筛选它们?
2 回答
我已经编辑了模型,因此它现在包含一个字段
Id
并将其用作主键 . 其他一切,包括平等比较,都保持不变 . 我现在收到一条不同的错误消息,可能会对此问题有所了解:我在datacontext中记录了SQL语句,这是最后执行的语句:
这表明没有填写
LanguageId
(表Language
中的字段Id
) . 这是有道理的,因为它默认为0,而我所做的就是将它附加到EF配置 . 这不会尝试创建对ID为0且不存在的条目的引用 .知道了这一点,我就选择了我所拥有的和我的目标 . 首先,我看一下该语言是否已经在数据库中 . 如果不是,一切都保持正常,我只需插入它 . 如果它已经在那里,我将其ID分配给新的
Language
对象,分离现有对象并附加新对象 .基本上我交换了EF跟踪的对象 . 如果它在注意到对象平等时会自己做这件事会非常有帮助但是直到它做到这一点,这是我想出的最好的 .
尝试在
Language
实体上实现IEquatable<T>
接口(我假设Iso
是实体主键):现在用这个,再试一次:
由于EF对从上下文加载的实体实例使用动态代理,我想知道
Contains
是否作为意外的false
值返回 . 我相信Contains
只会进行参考比较,而不是Equals
比较 . 由于从上下文检索的实体是动态代理实例,并且movie.SpokenLanguages
不是,Contains
可能没有按预期进行比较 .参考:https://msdn.microsoft.com/en-us/library/ms131187(v=vs.110).aspx