Сначала вручную обновляется связь «многие ко многим» в коде Entity Framework - PullRequest
0 голосов
/ 12 марта 2012

Хотя таблицы ссылок, которые облегчают отношения многие ко многим, как правило, скрыты 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 для Template как нулевой, а затем попытался сохранить его, что нарушило бы ограничение Required.

Я очень новичок в Entity Framework, так что для меня это все путешествие в путешествие, поэтому я полностью ожидаю, что в моем подходе будут ошибки или как я делаю обновление!

Заранее спасибо.

1 Ответ

3 голосов
/ 13 марта 2012

Вы должны отобразить отношения в вашей модели как два отношения один ко многим. Дополнительное поле в таблице ссылок делает невозможным создание отношения «многие ко многим». Я также рекомендовал бы использовать составной ключ в вашей «сущности ссылки» 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

...