如何安全高效地在实体框架 6 中为仅更改字段插入/更新记录



我正在尝试使用实体框架 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);

相关内容

  • 没有找到相关文章

最新更新