【问题标题】:Entity Framework 6.1 Updating a Subset of a Record实体框架 6.1 更新记录的子集
【发布时间】:2014-05-09 14:11:07
【问题描述】:

我有一个仅封装一些数据库模型属性的视图模型。视图模型包含的这些属性是我要更新的唯一属性。我希望其他属性保持其价值。

在我的研究过程中,我发现this 答案似乎非常适合我的需求,但是,尽管我尽了最大努力,但我无法让代码按预期工作。

这是我想出的一个孤立的例子:

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; }
}

我做错了什么?

【问题讨论】:

  • 你为什么要附加这个人?通常,当我使用实体框架时,我只是检索记录,修改其属性并执行 SaveChanges();类似于: Person person = context.People.First(); person.Name = "约翰"; context.SaveChanges();
  • 因为这需要对数据库进行两次查询 + 实际上,我使用的是通用存储库。
  • 这并不是一个真正的通用存储库,但无论如何,@Areks 表示 +1。如果您担心 2 个查询,那么您还有其他问题
  • @mituw16 尽管如此,我还是想知道为什么这段代码不起作用。感谢您的 cmets。
  • 尝试 entry.State = EntityState.Modified;

标签: c# entity-framework entity-framework-6.1


【解决方案1】:

您的工作基于帖子https://stackoverflow.com/a/15339512/2015959,但在另一个线程中,未更改的字段(因此不在附加模型中)不是强制性的,这就是它起作用的原因。由于您的字段是必需的,因此您会收到此验证错误。

您的问题可以通过问题Entity Framework validation with partial updates提供的解决方案来解决

【讨论】:

  • 嗨,Radu,感谢您如此具体。你的第二个链接正是我遇到的问题。
【解决方案2】:

这是导致它无法保存的验证。您可以使用context.Configuration.ValidateOnSaveEnabled = false; 禁用验证,它会起作用。要验证特定字段,您可以致电var error = entry.Property(e =&gt; 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;
  }

【讨论】:

    【解决方案3】:

    (为清楚起见进行了编辑)

    上下文必须具有对象的完整副本才能强制执行业务规则。仅当附加对象填充了所有必要的属性或部分视图在更新之前与完整副本合并时,才会发生这种情况。

    我相信您想要做的事情在概念上是不可能的:进行这样的更新将需要保留的更改前副本,或者需要对数据库进行两次查询,因为业务层需要对象的完整副本进行验证。

    【讨论】:

    • 你能引用一个来源吗?我认为 this answer 另有建议。
    • 不是这样。您可以仅对修改的字段强制执行业务规则。看我的回答。
    • @jltrem- 是的,可以禁用记录验证,并且您可以逐个检查字段级别的验证(必需、范围等)属性。但是此方法不适用于具有记录级别规则(IValidatableObject 或 ValidateEntity)的对象,这两者都可能需要实体的所有值才能通过。无论哪种方式,绕过业务层验证都需要依靠数据库来确保不插入错误数据。我想我原来的帖子应该说“在负责任的设计中在概念上是不可能的”?
    • @esmoore68 在很多情况下需要对字段进行简单更新。如果您的数据库流量很大,那么减少查询数量是必不可少的。 EF 使以安全、谨慎的方式做事变得容易……这很棒。但是要扩展系统,您必须围绕所提供的简单/安全/方便的方式工作,利用您对数据和数据库的了解,并实施有效的更新。
    猜你喜欢
    • 2021-02-19
    • 2021-09-21
    • 1970-01-01
    • 2017-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-19
    相关资源
    最近更新 更多