Entity Framework 6.1 更新记录子集

16

我有一个视图模型,仅封装了数据库模型的部分属性。这些包含在视图模型中的属性是我想要更新的唯一属性。我希望其他属性保留它们的值。

在研究过程中,我找到了这个答案,看起来非常适合我的需求,但是,尽管我尽了最大努力,我无法使代码按照预期工作。

这里是一个孤立的例子:

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

我做错了什么?


1
你为什么要攻击这个人?通常当我使用实体框架时,我只需检索记录,修改其属性并执行SaveChanges(); 就像这样:Person person = context.People.First(); person.Name = "John"; context.SaveChanges(); - user1162766
因为这需要在数据库上进行两个查询,而实际上我正在使用通用存储库。 - Caster Troy
1
那并不是真正的通用存储库,但无论如何,+1 给 @Areks 说的。如果你担心两个查询,那么你还有其他问题。 - mituw16
1
@mituw16 无论如何,我想知道为什么这段代码不起作用。感谢您的评论。 - Caster Troy
@CasterTroy,我的5月9日的回答已经说得很清楚了,你看过了吗?https://dev59.com/MmAg5IYBdhLWcg3wdayF#23567961 - jltrem
显示剩余10条评论
3个回答

4

嘿,Radu,感谢你的具体说明。你提供的第二个链接正是我遇到的问题。 - Jono

3

是验证导致它无法保存。您可以使用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;
  }

0

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

上下文必须拥有完整的对象副本以强制执行业务规则。只有在所附对象具有所有必要属性填充或在更新之前将部分视图与完整副本合并时,才能实现这一点。

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


请问您能否提供一个引用来源?我认为这个答案的意思是相反的。 - Caster Troy
1
不是这样的。您可以仅针对修改后的字段强制执行业务规则。请参见我的答案。 - jltrem
@jltrem- 是的,可以禁用记录验证,并且您可以逐个检查字段级别验证(必填、范围等)属性。但是,这种方法对于具有记录级规则(IValidatableObject或ValidateEntity)的对象无效,这两者都可能要求实体的所有值都通过。无论如何,绕过业务层验证需要回退到数据库,以确保不插入错误数据。我想我的原始帖子应该说“在负责任的设计中从概念上讲不可能”? - esmoore68
@esmoore68,许多情况下只需对字段进行简单更新。如果您的数据库流量很大,则减少查询次数至关重要。EF使安全,谨慎地完成任务变得容易......这很好。但是,为了扩展系统,您必须绕过提供的简便/安全/方便方法,利用您对数据和数据库的知识,实现高效更新。 - jltrem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接