2015-06-22 2 views
66

Когда я в отдельном сценарии и получаю dto от клиента, который я сопоставляю с сущностью для его сохранения, я делаю это:DbSet.Attach (entity) vs DbContext.Entry (entity) .State = EntityState.Modified

context.Entry(entity).State = EntityState.Modified; 
context.SaveChanges(); 

Для того, что тогда DbSet.Attach(entity)

или почему я должен использовать метод .Attach когда EntityState.Modified уже крепит сущность?

+0

Лучше добавьте некоторую информацию о версии, об этом просили раньше. Я не понимаю, заслуживает ли этого вопроса новый вопрос. –

ответ

158

Когда вы делаете context.Entry(entity).State = EntityState.Modified;, вы не только присоединяете объект к DbContext, но также отмечаете всю сущность как грязную. Это означает, что когда вы делаете context.SaveChanges(), EF сгенерирует оператор обновления, который будет обновлять все поля объекта.

Это не всегда желательно.

С другой стороны, DbSet.Attach(entity) прикрепляет сущность к контексту без маркировки она грязная. Это эквивалентно выполнению context.Entry(entity).State = EntityState.Unchanged;

При прикреплении этого способа, если вы не приступите к обновлению объекта на объекте, при следующем вызове context.SaveChanges() EF не будет генерировать обновление базы данных для этого объекта.

Даже если вы планируете сделать обновление для объекта, если у объекта много свойств (столбцы db), но вы хотите обновить несколько, вам может быть полезно сделать DbSet.Attach(entity), а затем обновите только несколько свойств, требующих обновления. Выполнение этого способа приведет к созданию более эффективного оператора обновления из EF. EF будет только обновлять измененные вами свойства (в отличие от context.Entry(entity).State = EntityState.Modified;, что приведет к обновлению всех свойств/столбцов)

Соответствующая документация: Add/Attach and Entity States.

Пример кода

Допустим, у вас есть следующий объект:

public class Person 
{ 
    public int Id { get; set; } // primary key 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

Если ваш код выглядит следующим образом:

context.Entry(personEntity).State = EntityState.Modified; 
context.SaveChanges(); 

SQL, генерироваться будет выглядеть примерно так:

UPDATE person 
SET FirstName = 'whatever first name is', 
    LastName = 'whatever last name is' 
WHERE Id = 123; -- whatever Id is. 

Обратите внимание, что указанный выше оператор обновления обновит все столбцы независимо от того, действительно ли вы изменили значения или нет.

В противоположность этому, если ваш код использует «нормальный» Attach так:

context.People.Attach(personEntity); // State = Unchanged 
personEntity.FirstName = "John"; // State = Modified, and only the FirstName property is dirty. 
context.SaveChanges(); 

Затем сгенерированный оператор обновления отличается:

UPDATE person 
SET FirstName = 'John' 
WHERE Id = 123; -- whatever Id is. 

Как вы можете видеть, оператор обновления только обновляет значения, которые были фактически изменены после присоединения объекта к контексту. В зависимости от структуры вашей таблицы это может оказать положительное влияние на производительность.

Теперь, какой вариант лучше для вас, целиком зависит от того, что вы пытаетесь сделать.

+9

Хороший ответ с подробностями! – Elisabeth

+0

EF не генерирует предложение WHERE таким образом. Если вы привязали объект, созданный с новым (то есть новым Entity()) и установили его для изменения, вам нужно установить все исходные поля из-за оптимистической блокировки. Предложение WHERE, сгенерированное в запросе UPDATE, обычно содержит все исходные поля (не только Id), поэтому, если вы этого не сделаете, EF выдает исключение параллелизма. – bubi

+1

@budi: Благодарим вас за отзыв. Я проверял, чтобы убедиться, и для основной сущности он ведет себя так, как я описал, с предложением WHERE, содержащим только первичный ключ, и без какой-либо проверки параллелизма. Для проверки параллелизма мне нужно явно настроить столбец как токен параллелизма или rowVersion. В этом случае предложение WHERE должно содержать только первичный ключ и столбец токенов параллелизма, а не все поля. Если ваши тесты показывают иное, я хотел бы услышать об этом. – sstan

0

Когда вы используете метод DbSet.Update, Entity Framework отмечает все свойства вашей организации как EntityState.Modified, поэтому отслеживает их. Если вы хотите изменить только некоторые из ваших свойств, не все из них, используйте DbSet.Attach. Этот метод делает все ваши свойства EntityState.Unchanged, поэтому вы должны сделать свои свойства, которые хотите обновить EntityState.Modified. Таким образом, когда приложение достигает DbContext.SaveChanges, оно будет работать только с измененными свойствами.

Смежные вопросы