MVC 3 EF 4.1 dbContext - удаление объекта данных «один ко многим» с ненулевым отношением внешнего ключа - PullRequest
14 голосов
/ 05 мая 2011

Я использую MVC 3, EF 4.1 и dbContext.Мне нужно знать, как удалить сущность в отношении «один ко многим» с ненулевым внешним ключом.

Когда я удаляю дочернюю сущность и выполняю SaveChanges, я получаю ошибку:

Операция не выполнена: отношение не может быть изменено, так как одно или несколько свойств внешнего ключа не могут иметь значение NULL.Когда в отношение вносится изменение, для соответствующего свойства внешнего ключа устанавливается нулевое значение.Если внешний ключ не поддерживает нулевые значения, необходимо определить новое отношение, свойству внешнего ключа должно быть назначено другое ненулевое значение или несвязанный объект должен быть удален.

Из других сообщений,Я понимаю, что использование Remove (entity) помечает сущность для удаления.Во время SaveChanges EF устанавливает для внешнего ключа значение Null, и возникает вышеуказанная ошибка.

Я нашел несколько сообщений, которые используют DeleteObject для дочерней сущности, а не Remove;однако подход DeleteObject, похоже, был отброшен из-за добавления к dbContext и DbSet.

Я обнаружил, что сообщения, в которых предлагается изменить отношение внешнего ключа EDMX, могут иметь значение Nullable.Модификация EDMX - это хорошо, но всякий раз, когда выполняется Обновление модели для базы данных, эти изменения сбрасываются со счетов и должны применяться повторно.Не оптимально

В другом посте предлагалось создать прокси-объект с отношениями внешних ключей, установленными в Nullable, но я не понимаю такой подход.Кажется, он страдает от той же проблемы, что и изменение EDMX, в том, что контекст автоматически обновляется при сохранении изменений в EDMX.

Моя упрощенная модель:

public partial class User
{
    public User()
    {
        this.UserContacts = new HashSet<UserContact>();
    }

    public long userId { get; set; }
    public string userEmail { get; set; }
    public string userPassword { get; set; }
    public string userFirstName { get; set; }
    public string userLastName { get; set; }
     . . .
    public virtual Country Country { get; set; }
    public virtual State State { get; set; }
    public virtual ICollection<UserContact> UserContacts { get; set; }
}

}

public partial class UserContact
{
    public long userContactId { get; set; }
    public long userContactUserId { get; set; }
    public long userContactTypeId { get; set; }
    public string userContactData { get; set; }

    public virtual ContactType ContactType { get; set; }
    public virtual User User { get; set; }
}

userContactUserId и userContactTypeId являются обязательными внешними ключами.

В контейнере dbContext и Users, и UserContact являются DbSet.

У меня есть ViewModel для пользователя и ViewModelдля UserContact:

public class UserContactViewModel
{
    [HiddenInput]
    public long UserContactId { get; set; }

    [HiddenInput]
    public long UserContactUserId { get; set; }

    [Display(Name = "Contact")]
    [Required]
    public string ContactData { get; set; }

    [Required]
    public long ContactType { get; set; }

    [HiddenInput]
    public bool isDeleted { get; set; }

}

    public class MyProfileViewModel
    {

        [HiddenInput]
        public long UserId { get; set; }

        [Required]
        [Display(Name = "First Name")]
        [StringLength(100)]
        public string FirstName { get; set; }

        [Required]
        [StringLength(100)]
        [Display(Name = "Last Name")]
        public string LastName { get; set; }
        ....
        public IEnumerable<UserContactViewModel> Contacts { get; set; }

}

При сохранении изменений в профиле пользователя я зацикливаюсь на списке объектов UserContactViewModel, чтобы определить, какие из них были добавлены, изменены или удалены.

                    foreach (var c in model.Contacts)
                    {
                        UserContact uc = usr.UserContacts.Single(con => con.userContactId == c.UserContactId);
                        if (uc != null)
                        {
                            if (c.isDeleted == true)  // Deleted UserContact
                            {
                                ctx.UserContacts.Remove(uc);  // Remove doesn't work
                            }
                            else  //  Modified UserContact
                            {
                                uc.userContactData = c.ContactData;
                                uc.userContactTypeId = c.ContactType;
                                ctx.Entry(uc).State = EntityState.Modified;
                            }
                        }
                        else  // New UserContact
                        {
                            usr.UserContacts.Add(new UserContact { userContactUserId = model.UserId, userContactData = c.ContactData, userContactTypeId = c.ContactType });
                        }
                    }

Буду признателен за любую помощь.

Ответы [ 4 ]

15 голосов
/ 19 июля 2011

Мне удалось решить проблему следующим образом:

Сначала я смог извлечь ObjectContext, приведя мой DbContext (например, "ctx") к IObjectContextAdapter, а затем получив ссылку на ObjectContext.

Затем я просто вызвал метод DeleteObject, передающий удаляемую запись UserContact.

Когда SaveChanges получает удаление в базе данных, как и ожидалось.

if (c.isDeleted == true)  // Deleted UserContact
{
    ObjectContext oc = ((IObjectContextAdapter)ctx).ObjectContext;
    oc.DeleteObject(uc)
}

Вотфрагмент соответствующего кода:

foreach (var c in model.Contacts)
{
    UserContact uc = null;
    if (c.UserContactId != 0)
    {
        uc = ctx.UserContacts.Find(c.UserContactId);
    }
    if (uc != null)
    {
        if (c.isDeleted == true)  // Deleted UserContact
        {
            ObjectContext oc = ((IObjectContextAdapter)ctx).ObjectContext;
            oc.DeleteObject(uc);
        }
        else  //  Modified UserContact
        {
            uc.userContactData = c.ContactData;
            uc.userContactTypeId = c.ContactType;
            ctx.Entry(uc).State = EntityState.Modified;
        }
    }
    else  // New UserContact
    {
        usr.UserContacts.Add(new UserContact { userContactData = c.ContactData, userContactTypeId = c.ContactType });
    }
}

ctx.Entry(usr).State = EntityState.Modified;
ctx.SaveChanges();

Надеюсь, это поможет кому-то в будущем.

3 голосов
/ 21 мая 2011

Я решил ту же проблему, следуя разделу «Правила каскадного удаления в отношениях» на странице руководства MSDN здесь http://msdn.microsoft.com/en-us/library/bb738695.aspx Надеюсь, что вы будете полезны: D

1 голос
/ 05 мая 2011

хорошо, внедрите свою собственную коллекцию ICollection и отметьте эти дочерние объекты для удаления вместе с удалением.А затем, в своем собственном переопределении метода SaveChanges, удалите эти объекты.

0 голосов
/ 05 мая 2011

Вы можете настроить отношение к каскаду ... это распространит удаление на зависимые объекты.

Но это очень опасно:)

Я предпочитаю устанавливать флаг в строке, который не позволяет уровню данных включать его в будущие запросы, большинство приложений не нуждаются в физическом удалении (и будет возможность отменить).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...