当我想保存从表单发回的对象,但从生成的 SQL 语句中排除特定字段时,会出现问题。在这种情况下,我不想在用户编辑表单中包含密码字段。
编辑 HttpPost Controller 方法包含以下内容:
db.Users.Attach(user);
db.Entry(user).State = EntityState.Modified;
db.Entry(user).Property(x => x.Password).IsModified = false;
db.SaveChanges();
我假设 .IsModified 语句意味着生成的 SQL 将包含一个没有密码字段的 UPDATE 语句,因此不会用数据库中的 null 覆盖现有密码值。
然而,事实并非如此,EF 的工作方式似乎略有不同。我的用户类及其关联的元数据类没有指定密码字段是必需的,但是在执行时,调用 SaveChanges 时会抛出“需要密码字段”DbEntityValidationException。
一种解决方案是将密码字段及其值作为隐藏表单字段包含在内,这在大多数情况下就足够了,但在这种情况下不是一个好主意,因为密码、散列或其他方式是相当敏感的数据。
在进一步检查时,似乎在我们使用数据库优先方法的这种情况下,.edmx 文件在密码字段上包含“not null”指令。当 EF 开始执行实际保存时,将对此进行检查并引发异常。
在我看来这并不完全直观,但一旦知道它就可以变通。
我的解决方案是为排除的字段提供一个值并保留 .IsModified 语句。这意味着 DbEntityValidationException 不会被抛出,因为 EF 看到密码字段包含一个值(如 .edmx 文件中所规定的),但由于 IsModified 为假,因此不会实际写入数据库。例如
user.Password = "not required";
db.Users.Attach(user);
db.Entry(user).State = EntityState.Modified;
db.Entry(user).Property(x => x.Password).IsModified = false;
db.SaveChanges();
是否有更好的解决方案,因为使用上面的虚拟值似乎有点 hack?
最佳答案
您还没有真正解释为什么您不想保存密码字段,我假设它只是因为您不知道该值是什么,并且在您当前的代码中它被设置为 NULL。
您可以使用 ViewModel 与您的网页绑定(bind),并且只提供所需的字段(因此在您的情况下,不是密码字段)。当用户 POST 返回数据时,您可以从数据库加载用户,仅设置您需要的字段,然后将数据保存回来。密码字段将保持不变。
例如,假设用户表是这样的:
ID
UserName
Password
FullName
你会有一个 View 模型类
public class UserViewModel
{
public int ID { get; set; }
public string UserName { get; set; }
public string FullName { get; set; }
}
当数据从客户端回发到服务器时:
using (MyEntity db = new MyEntity())
{
User u = db.Users.Find(userViewModel.ID);
u.UserName = userViewModel.UserName;
u.FullName = userViewModel.FullName;
db.SaveChanges();
}
关于c# - 执行 SaveChanges 时排除未修改的字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35458980/