我正在尝试使用实体框架 6 安全地插入/更新实体。 我想我不会使用非线程安全且不建议用于生产环境的 AddOrUpdate 方法,而是首先尝试在我的数据库中插入实体,如果由于主键冲突而失败,那么我会改为进行更新。 我使用了数据库优先模型。 我的实体是主键 = 用户 ID 的用户,所有其他字段均可为空。 我需要更新方案来返回更新的实体,其中 POST 值超过相应的值,而所有其他值则保持数据库中的状态。
例如,我的用户实体有 20 个属性。 我的 POST 只能更新给定用户 ID 的这些属性的一小部分。 我需要返回的用户显示更新的用户。
例如,我首先执行一个 POST 来插入一个用户 ID = x1 的新用户,如下所示:
{ "用户 ID": "x1", "名字": "用户名字", "姓氏": "用户姓氏", "电子邮件": "email@company.com"}
我的第二个帖子是该用户更新他们的电子邮件的更新,如下所示:
{ "用户 ID": "x1", "电子邮件": "different_email@xyz.com"}
我需要第二个 POST 向我显示我在第一个插入 POST 中设置的原始名字和姓氏,而不是 NULL。 我在数据库中看到了原始的名字和姓氏,但它没有显示在第二个 POST 的响应中。
问题是:(1)我在这里做错了什么?(2) 我正在向数据库进行 2-3 次查询。 有没有一种更干净的方法来减少到数据库的往返次数,而不会影响线程安全性和并发性?
private UCBContext db = new UCBContext();
private bool InsertUser(ref User user)
{
try
{
db.Users.Add(user);
db.SaveChanges();
return (true);
}
catch { }
return (false);
}
private bool UpdateUser(ref User user)
{
try
{
db.Users.Attach(user);
DbEntityEntry entry = db.Entry(user);
foreach (var propertyName in entry.CurrentValues.PropertyNames)
{
var value = entry.CurrentValues[propertyName];
entry.Property(propertyName).IsModified = (propertyName != "UserID" && value != null);
}
db.SaveChanges();
return (true);
}
catch { }
return (false);
}
// POST: api/users = Insert/Update User
[ResponseType(typeof(User))]
public IHttpActionResult PostUser(User user)
{
if (!ModelState.IsValid){ return BadRequest(ModelState); }
bool ok = InsertUser(ref user);
if (!ok) { ok = UpdateUser(ref user); }
User dbuser = db.Users.Find(user.UserID);
if(dbuser == null) { return NotFound(); }
return Ok(dbuser);
}
public partial class User
{
public User()
{
this.Logins = 0;
}
public string UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public Nullable<int> Logins { get; set; }
}
倒数第二行返回原始用户对象...你应该做:
return Ok(dbuser);
而不是:
return Ok(user);
只是为了添加更多的颜色 - 请记住,当您将用户对象传递给InsertUser或UpdateUser时,您实际上是在传递它的副本。这就是为什么您需要根据 UserID 重新加载,然后返回此刷新的用户对象。
这是一个稍微不同的方法。我还没有测试过这段代码。如果它不起作用,请告诉我们。
try
{
var existingUser = db.Users.Find(user.UserId); //or use db.Users.FirstOrDefault(...)
if(existingUser == null)
{
return false;
}
Type nType = user.GetType();
PropertyInfo[] newValues = nType.GetProperties();
foreach (PropertyInfo prop in newValues)
{
var propVal = prop.GetValue(user,null);
if(propVal!= null)
{
var eProp = existingUser.GetType().GetProperty(prop.Name);
if(eProp != null)
{
eProp.SetValue(existingUser, propVal, null);
}
}
}
db.SaveChanges();
return (true);
}
catch { }
return (false);