Entity Framework 6.1 Обновление подмножества записи

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

Во время моего исследования я нашел этот ответ, который кажется идеальным для моих нужд, однако, несмотря на все мои усилия, я не могу получить код для работы, как ожидалось.

Вот отдельный пример того, что я придумал:

static void Main() {
    // Person with ID 1 already exists in database.

    // 1. Update the Age and Name.
    Person person = new Person();
    person.Id = 1;
    person.Age = 18;
    person.Name = "Alex";

    // 2. Do not update the NI. I want to preserve that value.
    // person.NINumber = "123456";

    Update(person);
}

static void Update(Person updatedPerson) {
    var context = new PersonContext();

    context.Persons.Attach(updatedPerson);
    var entry = context.Entry(updatedPerson);

    entry.Property(e => e.Name).IsModified = true;
    entry.Property(e => e.Age).IsModified = true;

    // Boom! Throws a validation exception saying that the 
    // NI field is required.
    context.SaveChanges();
}

public class PersonContext : DbContext {
    public DbSet<Person> Persons { get; set; }
}

public class Person {
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required] 
    public int Age { get; set; } // this is contrived so, yeah.
    [Required]
    public string NINumber { get; set; }
}

Что я делаю не так?


person Caster Troy    schedule 09.05.2014    source источник
comment
Зачем ты привязываешь человека? Обычно, когда я работаю с инфраструктурой сущностей, я просто извлекаю запись, изменяю ее свойства и выполняю SaveChanges(); Что-то вроде: Person person = context.People.First(); person.Name = Джон; контекст.Сохранить изменения();   -  person Areks    schedule 09.05.2014
comment
Поскольку для этого потребуется два запроса к базе данных + на самом деле я использую общий репозиторий.   -  person Caster Troy    schedule 09.05.2014
comment
На самом деле это не общий репозиторий, но в любом случае +1 для @Areks сказал. Если вас беспокоят 2 запроса, у вас есть другие проблемы   -  person mituw16    schedule 09.05.2014
comment
@ mituw16 Тем не менее, я хотел бы знать, почему этот код не работает. Спасибо за ваши комментарии.   -  person Caster Troy    schedule 09.05.2014
comment
попробуйте entry.State = EntityState.Modified;   -  person Radu Pascal    schedule 09.05.2014
comment
@RaduPascal Установка для свойства State значения EntityState.Modified не помогает. Спасибо, в любом случае.   -  person Caster Troy    schedule 09.05.2014
comment
Я не совсем понимаю, почему вы даже вручную устанавливаете отдельные свойства. После того, как вы прикрепите объект, вам не нужно ничего делать, кроме context.Entry(updatedPerson).State = EntityDtate.Modified;, но это предполагает, что updatedPerson является объектом EF, а не просто обычным объектом класса.   -  person mituw16    schedule 09.05.2014
comment
@mituw16 mituw16 Потому что такой подход заставит EF попытаться обновить свойство NINumber (в этом случае оно станет null и вызовет ошибку проверки). Это то, чего я пытаюсь избежать.   -  person Caster Troy    schedule 09.05.2014
comment
нашел аналогичный вопрос с предлагаемым решением: stackoverflow.com/questions/12871892/   -  person Radu Pascal    schedule 09.05.2014
comment
@CasterTroy Верно, поэтому вам нужен первый запрос, чтобы получить действительный объект сущности человека, а затем вы обновляете только те свойства, которые вам нужно изменить.   -  person mituw16    schedule 09.05.2014
comment
@RaduPascal Это помогло мне, Раду. Спасибо чувак. Я до сих пор не уверен, зачем мне это нужно было делать, когда Ladu по-видимому этого не сделал и мне все еще интересно узнать, но, по крайней мере, сейчас я могу продолжить свой проект. Спасибо еще раз.   -  person Caster Troy    schedule 09.05.2014
comment
@CasterTroy, насколько я понимаю проблему из другого потока, заключается в том, что поля, которые не были изменены (и, как таковые, не были в прикрепленной модели), не были обязательными, и поэтому это сработало. Поскольку ваши поля обязательны, вы получите эту ошибку проверки.   -  person Radu Pascal    schedule 09.05.2014
comment
@RaduPascal Проклятый Раду. Ты убиваешь это, чувак. Огромное спасибо. Если вы опубликуете ответ, я с радостью приму.   -  person Caster Troy    schedule 14.05.2014
comment
@CasterTroy мой ответ от 9 мая сказал об этом; Вы читали его? stackoverflow.com/a/23567961/571637   -  person jltrem    schedule 14.05.2014
comment
рад, что помогает! Я добавил ответ, надеюсь, он инкапсулирует все важные моменты.   -  person Radu Pascal    schedule 14.05.2014


Ответы (3)


Вы основывали свою работу на сообщении https://stackoverflow.com/a/15339512/2015959, но в другом потоке поля, которые не были изменены (и как таковые не были в прикрепленной модели), не были обязательными, и поэтому это сработало. Поскольку ваши поля обязательны, вы получите эту ошибку проверки.

Ваша проблема может быть решена с помощью решения, предоставленного в вопросе Проверка Entity Framework с частичными обновлениями

person Radu Pascal    schedule 14.05.2014
comment
Привет, Раду, спасибо за такую ​​конкретику. Ваша вторая ссылка - это именно та проблема, с которой я столкнулся. - person Jono; 30.03.2016

Это проверка, из-за которой он не сохраняется. Вы можете отключить проверку с помощью context.Configuration.ValidateOnSaveEnabled = false;, и она будет работать. Чтобы проверить определенные поля, вы можете вызвать var error = entry.Property(e => e.Name).GetValidationErrors();. Таким образом, вы, безусловно, можете создать метод «UpdateNameAndAge», который только применяет только бизнес-правила и помечает эти свойства как измененные. Двойной запрос не требуется.

  private static bool UpdateNameAndAge(int id, string name, int age)
  {
     bool success = false;

     var context = new PersonContext();
     context.Configuration.ValidateOnSaveEnabled = false;

     var person = new Person() {Id = id, Name = name, Age = age};
     context.Persons.Attach(person);
     var entry = context.Entry(person);

     // validate the two fields
     var errorsName = entry.Property(e => e.Name).GetValidationErrors();
     var errorsAge = entry.Property(e => e.Age).GetValidationErrors();

     // save if validation was good
     if (!errorsName.Any() && !errorsAge.Any())
     {
        entry.Property(e => e.Name).IsModified = true;
        entry.Property(e => e.Age).IsModified = true;

        if (context.SaveChanges() > 0)
        {
           success = true;
        }
     }

     return success;
  }
person jltrem    schedule 09.05.2014

(Отредактировано для ясности)

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

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

person esmoore68    schedule 09.05.2014
comment
Не могли бы вы указать источник? Я думаю, что этот ответ предполагает иное. - person Caster Troy; 09.05.2014
comment
не так. вы можете применять бизнес-правила только для измененных полей. см. мой ответ. - person jltrem; 09.05.2014
comment
@jltrem- да, можно отключить проверку записи, и вы можете проверять свойство проверки на уровне поля (обязательное, диапазон и т. д.) по свойству. Но этот метод не будет работать для объекта с правилами уровня записи (IValidatableObject или ValidateEntity), оба из которых могут потребовать передачи всех значений объекта. В любом случае обход проверки бизнес-уровня требует возврата к базе данных, чтобы убедиться, что неверные данные не вставлены. Я полагаю, в моем первоначальном посте должно было быть сказано: «Концептуально невозможно в ответственном дизайне»? - person esmoore68; 09.05.2014
comment
@ esmoore68 есть много случаев, когда требуется простое обновление поля. Если ваша БД получает большой трафик, важно уменьшить количество запросов. EF позволяет легко делать вещи безопасным и осторожным образом... и это здорово. Но чтобы масштабировать систему, вам нужно обойти предложенный простой/безопасный/удобный способ, использовать свои знания о ваших данных и базе данных и внедрить эффективное обновление. - person jltrem; 09.05.2014