首先在实体框架代码中手动更新多对多关系

时间:2012-03-12 11:20:12

标签: entity-framework entity-framework-4 ef-code-first

虽然促进多对多关系的链接表通常由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很新,所以这对我来说都是一次发现之旅,所以我完全预料到我的方法会出现错误或者我是如何进行更新的!

提前致谢。

1 个答案:

答案 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