虽然促进多对多关系的链接表通常由EF隐藏,但我有一个实例,我想我需要自己创建(和管理)一个:
我有以下实体:
public class TemplateField
{
public int Id
{
get;
set;
}
[Required]
public string Name
{
get;
set;
}
}
public class TemplateFieldInstance
{
public int Id
{
get;
set;
}
public bool IsRequired
{
get;
set;
}
[Required]
public virtual TemplateField Field
{
get;
set;
}
[Required]
public virtual Template Template
{
get;
set;
}
}
public class Template
{
public int Id
{
get;
set;
}
public string Name
{
get;
set;
}
public virtual ICollection<TemplateFieldInstance> Instances
{
get;
set;
}
}
基本上; Template
可以包含多个TemplateField
,而TemplateField
可以包含多个Template
。
我相信我可以在Template
实体上以TemplateField
项集合的形式添加导航属性,并让EF管理链接实体,但我需要存储一些额外的信息关系,因此IsRequired
上的TemplateFieldInstance
属性。
我遇到的实际问题是更新Template
时。我正在使用类似于以下内容的代码:
var template = ... // The updated template.
using (var context = new ExampleContext())
{
// LoadedTemplates is just Templates with an Include for the child Instances.
var currentTemplate = context.LoadedTemplates.Single(t => t.Id == template.Id);
currentTemplate.Instances = template.Instances;
context.Entry(currentTemplate).CurrentValues.SetValues(template);
context.SaveChanges();
}
然而;如果我尝试将Template
更新为 - 例如 - 删除其中一个TemplateFieldInstance
实体,则会引发异常(带有内部异常),该异常表明:
来自'TemplateFieldInstance_Template'的关系 AssociationSet处于“已删除”状态。鉴于多样性 约束,相应的'TemplateFieldInstance_Template_Source' 也必须处于'已删除'状态。
在做了一些研究之后,听起来这是因为EF基本上将TemplateFieldInstance
的{{1}}外键标记为null,然后尝试保存它,这会违反{{1约束。
我对Entity Framework很新,所以这对我来说都是一次发现之旅,所以我完全预料到我的方法会出现错误或者我是如何进行更新的!
提前致谢。
答案 0 :(得分:3)
您必须将模型中的关系映射为两个一对多关系。链接表中的附加字段使得无法创建多对多关系。我还建议在“链接实体”TemplateFieldInstance
中使用复合键,其中两个组件都是其他实体的外键。这样可以确保在数据库中只有一行用于模板字段和模板的唯一组合,并且最接近“具有附加数据的多对多链接表”的概念:
public class TemplateField
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<TemplateFieldInstance> Instances { get; set; }
}
public class TemplateFieldInstance
{
[Key, Column(Order = 0)]
public int FieldId { get; set; }
[Key, Column(Order = 1)]
public int TemplateId { get; set; }
public bool IsRequired { get; set; }
public virtual TemplateField Field { get; set; }
public virtual Template Template { get; set; }
}
public class Template
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<TemplateFieldInstance> Instances { get; set; }
}
如果使用上面的属性名称,EF命名约定将检测此模型中的FK关系。
有关此类型号的更多详情,请访问:https://stackoverflow.com/a/7053393/270591
更新模板的方法不正确:context.Entry(currentTemplate).CurrentValues.SetValues(template);
只会更新模板的标量字段,而不会更新导航属性,也不会添加或删除父实体的任何新的或已删除的子实体。不幸的是,更新分离的对象图并不是那么容易,你必须编写更多代码,如下所示:
var template = ... // The updated template.
using (var context = new ExampleContext())
{
// LoadedTemplates is just Templates with an Include for the child Instances.
var currentTemplate = context.LoadedTemplates
.Single(t => t.Id == template.Id);
context.Entry(currentTemplate).CurrentValues.SetValues(template);
foreach (var currentInstance in currentTemplate.Instances.ToList())
if (!template.Instances.Any(i => i.Id == currentInstance.Id))
context.TemplateFieldInstances.Remove(currentInstance); // DELETE
foreach (var instance in template.Instances)
{
var currentInstance = currentTemplate.Instances
.SingleOrDefault(i => i.Id == instance.Id);
if (currentInstance != null)
context.Entry(currentInstance).CurrentValues.SetValues(instance);
// UPDATE
else
currentTemplate.Instances.Add(instance); // INSERT
}
context.SaveChanges();
}
更多评论的类似示例如下:https://stackoverflow.com/a/5540956/270591