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

ользую 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 Z15 июн. 2011 г., 22:32
Вы когда-нибудь находили решение для этого? У меня точно такая же проблема ..
 Max18 июл. 2011 г., 23:01
Я нашел решение, см. Мой ответ на пост ниже.
 Kyle Rogers22 июн. 2011 г., 17:02
@B Z ты когда-нибудь находил решение? У меня та же проблема?
 Kyle Rogers22 июн. 2011 г., 17:02
Вы когда-нибудь находили решение? У меня та же проблема?

Ответы на вопрос(4)

Решение Вопроса

Во-первых, я смог извлечь 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 Sotolar01 июл. 2012 г., 15:54
работает как шарм .. чем ты.

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

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

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

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

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

Ваш ответ на вопрос