Вопрос по entity-framework, asp.net-mvc-3 – Я предпочитаю устанавливать флаг в строке, который не позволяет уровню данных включать его в будущие запросы, большинство приложений не нуждаются в физическом удалении (и будет возможность отменить).

14

ользую 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 });
                        }
                    }

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

@B Z ты когда-нибудь находил решение? У меня та же проблема? Kyle Rogers
Вы когда-нибудь находили решение? У меня та же проблема? Kyle Rogers
Я нашел решение, см. Мой ответ на пост ниже. Max
Вы когда-нибудь находили решение для этого? У меня точно такая же проблема .. B Z

Ваш Ответ

4   ответа
15

Во-первых, я смог извлечь 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();

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

работает как шарм .. чем ты. Ondrej Sotolar
3

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

1

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

0

на зависимые объекты.

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

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

Похожие вопросы