问题
let's say we have a Person class(with properties of Id and Age), let's say we need to change the Age of a person whose id is 1 from 29 to 30, and I use DbSet.Update()
and DbSet.Attach()
respectively:
Person p = new Person(){ Id = 1 }
var entity = context.Persons.Update(p);
p.Age = 30;
Console.WriteLine("entity state:" + entity.State);
foreach (var modifiedProperty in entity.Properties.Where(p => p.IsModified))
{
Console.Write($"The {modifiedProperty.Metadata.Name} property is marked as modified,");
}
context.SaveChanges();
output:
entity state:Modified.
The Age property is marked as modified,The Name property is marked as modified*
which is expected result, but if I use Attach()
as
Person p = new Person(){ Id = 1 }
var entity = context.Persons.Attach(p);
p.Age = 30;
Console.WriteLine("entity state:" + entity.State);
foreach (var modifiedProperty in entity.Properties.Where(p => p.IsModified))
{
Console.Write($"The {modifiedProperty.Metadata.Name} property is marked as modified,");
}
context.SaveChanges();
output:
entity state:Unchanged.
and nothing after, since no properties has been identified as modified, but I changed the Age property the same way as in Update() method, why Attach()
cannot identify modified properties? if Attach()
cannot identify modified properties, how come it still generate the correct update sql statement to database?
回答1:
You are very much confusing what is in sql server with what is in the context cache. The DbContext caches entities and tracks them so it knows what to do when SaveChanges()
is called. If the entity does not exist in the cache, it is up to the developer to help entity-framework determine the correct logic.
DbSet.Update(TEntity) Method
Begins tracking the given entity in the Modified state such that it will be updated in the database when SaveChanges() is called.
DbSet.Attach(TEntity) Method
Begins tracking the given entity in the Unchanged state such that no operation will be performed when SaveChanges() is called.
Onto your point:
but I changed the Age property the same way as in Update() method
Yes you did, but that does not change how you attached it. Order of events is very important. In the following example, we change Age to 31 before we attach the entity to the Entity Framework Context. It does not know you changed it was not tracking it at the time the change occurred (record will NOT change to 31):
using (var context = new EntityContext())
{
p.Age = 31;
context.Persons.Attach(p);
context.SaveChanges();
}
if Attach() cannot identify modified properties, how come it still generate the correct update sql statement to database
If we change the order of events to Attach the entity before we change a values, then Entity Framework can detect the change an save it to the DB (record will CHANGE to 32):
using (var context = new EntityContext())
{
context.Persons.Attach(p);
p.Age = 32;
context.SaveChanges();
}
All Working Examples:
DotNetFiddle Core Example
// Entity Framework Extensions
// Doc: https://entityframework-extensions.net/context-factory
// @nuget: Microsoft.EntityFrameworkCore
// nuget: Z.EntityFramework.Extensions.EFCore
// @nuget: Microsoft.EntityFrameworkCore.SqlServer
// @nuget: Microsoft.Extensions.Logging
using System;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.Extensions.Logging;
using System.Linq;
public class Program
{
public static void Main()
{
var p = new Person();
using (var context = new EntityContext())
{
context.Database.EnsureCreated();
p.Age = 29;
context.Persons.Add(p);
context.SaveChanges();
}
using (var context = new EntityContext())
{
Console.WriteLine("Created Person Age 29");
FiddleHelper.WriteTable("Student", context.Persons.ToList());
}
using (var context = new EntityContext())
{
p.Age = 30;
context.Persons.Update(p);
context.SaveChanges();
}
using (var context = new EntityContext())
{
Console.WriteLine("Update Person Age 30");
FiddleHelper.WriteTable("Student", context.Persons.ToList());
}
using (var context = new EntityContext())
{
p.Age = 31;
context.Persons.Attach(p);
context.SaveChanges();
}
using (var context = new EntityContext())
{
Console.WriteLine("Change Person to 31, THEN Attach");
FiddleHelper.WriteTable("Student", context.Persons.ToList());
}
using (var context = new EntityContext())
{
context.Persons.Attach(p);
p.Age = 32;
context.SaveChanges();
}
using (var context = new EntityContext())
{
Console.WriteLine("Attach THEN Change Person to 32");
FiddleHelper.WriteTable("Student", context.Persons.ToList());
}
}
public class EntityContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString = FiddleHelper.GetConnectionStringSqlServer();
optionsBuilder.UseSqlServer(connectionString);
}
public DbSet<Person> Persons { get; set; }
}
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
}
来源:https://stackoverflow.com/questions/57471685/differences-in-marking-modified-properties-between-dbset-attach-and-dbset-upda