我有以下课程:
public class Foo
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
public float Quantity { get; set; }
}
我正在使用以下代码更新属性Quantity
:
Foo f = new Foo {ID = 2, Quantity = 10}; // Updated object
DbSet dbSet = this.Set(f.GetType());
dbSet.Attach(f);
var be = Entry(f);
be.Property("Quantity").IsModified = true;
DBContext.SaveChanges();
但是,代码会产生如下异常:
“字段'名称'是必需的”
据我所知,最后一段代码应该只更新字段Quantity
,但它也会考虑字段Name
。
我可以通过在更新之前获取对象来避免此错误
Foo f = DBContext.Foos.Single<Foo>( x => x.ID == 2);
f.Quantity = 10;
DBContext.SaveChanges();
但我认为这不是一个好的选择,因为它将花费两次数据库访问。
任何建议将不胜感激。
答案 0 :(得分:2)
您的代码没问题。唯一的问题是EF验证过程检查所有属性(也是未更改的属性)。因此,如果设置了Name,EF将在SET子句中生成一个没有Name的查询。
我的意思是,如果你运行这段代码
update [Foos]
set [Quantity] = @p0
where ([ID] = @p1)
@p0 = 10
@p1 = 2
EF将运行此查询
php artisan queue:work
答案 1 :(得分:2)
正如其他人所指出的那样,您可以为其所需属性创建一个具有虚拟值的存根实体。但是,您似乎有几个具有Quantity
属性的类,您希望以这种方式更新。必须知道这些类的所有必需属性是非常麻烦的。
我认为更好的选择是通过添加一行来完全关闭验证:
DBContext.Configuration.ValidateOnSaveEnabled = false;
在DBContext.SaveChanges();
之前。
我假设您按照建议简要使用上下文,因此无需再次启用验证。
不验证Quantity
是安全的。它是一个不可为空的float
,所以你不能无意中为它设置一个空值。但请注意,当您需要其他验证[Required]
时,例如最大值,您必须明确添加这些验证。
更高级的替代方法是覆盖上下文的ShouldValidateEntity
方法,使其跳过只有一个名为“Quantity”的修改属性的实体:
protected override bool ShouldValidateEntity(DbEntityEntry entityEntry)
{
if (entityEntry.State == System.Data.Entity.EntityState.Modified)
{
var ose = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager
.GetObjectStateEntry(entityEntry.Entity);
var modifiedProperties = ose.GetModifiedProperties();
var isValidated = modifiedProperties.Count() == 1
&& modifiedProperties.Any(p => p == "Quantity");
return !isValidated;
}
return base.ShouldValidateEntity(entityEntry);
}
如果需要,您也可以将此行为切换为可切换,例如向您的上下文添加一些布尔属性。
答案 2 :(得分:1)
您的类包含Required
属性,但您不在代码中填充此字段:
Foo f = new Foo {ID = 2, Quantity = 10};
用以下代码替换您的代码:
Foo f = new Foo {ID = 2, Name = "Define name of Foo", Quantity = 10};
请注意,属性Name
初始化为值。
答案 3 :(得分:0)
虽然不是EF解决方案,但您可以使用Drapper非常轻松地完成此操作。
您向Drapper提供要执行的更新语句,它将执行它。 E.g:
public Foo Update(Foo foo)
{
return _Execute(foo) ? foo : null;
}
简单。简单。快速。没有箍跳&amp;你有完全的控制权。
答案 4 :(得分:0)
没有必要使用输入选项。 Attach将为您完成工作,但需要添加一个虚拟Name
值才能通过EF验证
Foo f = new Foo {ID = 2, Name='-'};
DBContext.Foos.Attach(f);
f.Quantity = 10;
DBContext.SaveChanges();
字段Name
不会更新,但在Attach(f)
方法