如何安全有效地在Entity Framework 6中为仅更改字段插入/更新记录?

时间:2016-03-07 03:57:43

标签: c# entity-framework entity-framework-6 ef-database-first

我正在尝试使用Entity Framework 6安全地插入/更新实体。我没有使用非线程安全且不建议用于生产的AddOrUpdate方法,我想我会先尝试在我的数据库中插入实体,如果由于主键冲突而失败,那么我会改为更新。我使用了数据库第一个模型。我的实体是一个用户,其主键= UserID,所有其他字段都可以为空。我需要Update Scenario来返回更新的实体,其中POST的值超过相应的值,而其他所有值都保留在DB中。

例如,我的用户实体有20个属性。我的POST可能只更新给定UserID的这些属性的一小部分。我需要返回的用户来显示更新的用户。

例如,我首先执行POST以插入UserID = x1的新用户,如下所示:

{   “UserID”:“x1”,   “FirstName”:“用户名”,   “LastName”:“用户姓”,   “电子邮件”:“email@company.com” }

我的第二个POST是该用户的更新,以更新他们的电子邮件:

{   “UserID”:“x1”,   “电子邮件”:“different_email@xyz.com” }

我需要第二个POST来向我显示我在第一个插入POST中设置的原始FirstName和LastName不是NULL。我在数据库中看到了原始的FirstName和LastName,但它没有显示在第二个POST的响应中。

问题是: (1)我在这里做错了什么? (2)我正在向DB发出2-3个查询。有没有更简洁的方法来减少到DB的往返而不影响线程安全性和并发性?

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

2 个答案:

答案 0 :(得分:0)

第二行返回原始用户对象...你应该这样做:

return Ok(dbuser);

而不是:

return Ok(user);

只是为此添加更多颜色 - 请记住,当您将用户对象传递给InsertUser或UpdateUser时,您基本上会传递它的副本。这就是为什么你需要根据UserID重新加载,然后返回这个刷新的用户对象。

答案 1 :(得分:0)

这是一种略有不同的方法。我还没有测试过这段代码。如果它不起作用,请告诉我们。

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