C# 实体框架 5 更新记录

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15336248/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 16:27:50  来源:igfitidea点击:

Entity Framework 5 Updating a Record

c#asp.net-mvc-3entity-framework-5

提问by Stokedout

I have been exploring different methods of editing/updating a record within Entity Framework 5 in an ASP.NET MVC3 environment, but so far none of them tick all of the boxes I need. I'll explain why.

我一直在探索在 ASP.NET MVC3 环境中的 Entity Framework 5 中编辑/更新记录的不同方法,但到目前为止,它们都没有勾选我需要的所有框。我会解释为什么。

I have found three methods to which I'll mention the pros and cons:

我找到了三种方法,我将提及其优缺点:

Method 1 - Load original record, update each property

方法 1 - 加载原始记录,更新每个属性

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    original.BusinessEntityId = updatedUser.BusinessEntityId;
    original.Email = updatedUser.Email;
    original.EmployeeId = updatedUser.EmployeeId;
    original.Forename = updatedUser.Forename;
    original.Surname = updatedUser.Surname;
    original.Telephone = updatedUser.Telephone;
    original.Title = updatedUser.Title;
    original.Fax = updatedUser.Fax;
    original.ASPNetUserId = updatedUser.ASPNetUserId;
    db.SaveChanges();
}    

Pros

优点

  • Can specify which properties change
  • Views don't need to contain every property
  • 可以指定哪些属性发生变化
  • 视图不需要包含每个属性

Cons

缺点

  • 2 x queries on database to load original then update it
  • 2 x 查询数据库以加载原始数据然后更新它

Method 2 - Load original record, set changed values

方法 2 - 加载原始记录,设置更改的值

var original = db.Users.Find(updatedUser.UserId);

if (original != null)
{
    db.Entry(original).CurrentValues.SetValues(updatedUser);
    db.SaveChanges();
}

Pros

优点

  • Only modified properties are sent to database
  • 仅将修改后的属性发送到数据库

Cons

缺点

  • Views need to contain every property
  • 2 x queries on database to load original then update it
  • 视图需要包含每个属性
  • 2 x 查询数据库以加载原始数据然后更新它

Method 3 - Attach updated record and set state to EntityState.Modified

方法 3 - 将更新的记录和设置状态附加到 EntityState.Modified

db.Users.Attach(updatedUser);
db.Entry(updatedUser).State = EntityState.Modified;
db.SaveChanges();

Pros

优点

  • 1 x query on database to update
  • 1 x 查询要更新的数据库

Cons

缺点

  • Can't specify which properties change
  • Views must contain every property
  • 无法指定更改哪些属性
  • 视图必须包含每个属性

Question

My question to you guys; is there a clean way that I can achieve this set of goals?

我对你们的问题;有没有一种干净的方法可以实现这组目标?

  • Can specify which properties change
  • Views don't need to contain every property (such as password!)
  • 1 x query on database to update
  • 可以指定哪些属性发生变化
  • 视图不需要包含每个属性(例如密码!)
  • 1 x 查询要更新的数据库

I understand this is quite a minor thing to point out but I may be missing a simple solution to this. If not method one will prevail ;-)

我知道这是一件很小的事情,但我可能缺少一个简单的解决方案。如果不是方法一将占上风;-)

采纳答案by Ladislav Mrnka

You are looking for:

您正在寻找:

db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();

回答by smd

I really like the accepted answer. I believe there is yet another way to approach this as well. Let's say you have a very short list of properties that you wouldn't want to ever include in a View, so when updating the entity, those would be omitted. Let's say that those two fields are Password and SSN.

我真的很喜欢接受的答案。我相信还有另一种方法可以解决这个问题。假设您有一个非常短的属性列表,您不想将这些属性包含在视图中,因此在更新实体时,这些属性将被省略。假设这两个字段是密码和 SSN。

db.Users.Attach(updatedUser);

var entry = db.Entry(updatedUser);
entry.State = EntityState.Modified;

entry.Property(e => e.Password).IsModified = false;
entry.Property(e => e.SSN).IsModified = false;   

db.SaveChanges();   

This example allows you to essentially leave your business logic alone after adding a new field to your Users table and to your View.

此示例允许您在向用户表和视图添加新字段后,基本上不考虑业务逻辑。

回答by Stefano Camisassi

foreach(PropertyInfo propertyInfo in original.GetType().GetProperties()) {
    if (propertyInfo.GetValue(updatedUser, null) == null)
        propertyInfo.SetValue(updatedUser, propertyInfo.GetValue(original, null), null);
}
db.Entry(original).CurrentValues.SetValues(updatedUser);
db.SaveChanges();

回答by Ian Warburton

I have added an extra update method onto my repository base class that's similar to the update method generated by Scaffolding. Instead of setting the entire object to "modified", it sets a set of individual properties. (T is a class generic parameter.)

我在我的存储库基类中添加了一个额外的更新方法,它类似于 Scaffolding 生成的更新方法。它不是将整个对象设置为“已修改”,而是设置一组单独的属性。(T 是类泛型参数。)

public void Update(T obj, params Expression<Func<T, object>>[] propertiesToUpdate)
{
    Context.Set<T>().Attach(obj);

    foreach (var p in propertiesToUpdate)
    {
        Context.Entry(obj).Property(p).IsModified = true;
    }
}

And then to call, for example:

然后调用,例如:

public void UpdatePasswordAndEmail(long userId, string password, string email)
{
    var user = new User {UserId = userId, Password = password, Email = email};

    Update(user, u => u.Password, u => u.Email);

    Save();
}

I like one trip to the database. Its probably better to do this with view models, though, in order to avoid repeating sets of properties. I haven't done that yet because I don't know how to avoid bringing the validation messages on my view model validators into my domain project.

我喜欢去数据库一趟。不过,使用视图模型可能会更好,以避免重复属性集。我还没有这样做,因为我不知道如何避免将我的视图模型验证器上的验证消息带入我的域项目。

回答by Bostwick

Just to add to the list of options. You can also grab the object from the database, and use an auto mapping tool like Auto Mapperto update the parts of the record you want to change..

只是为了添加到选项列表中。您还可以从数据库中获取对象,然后使用Auto Mapper 之类的自动映射工具 来更新要更改的记录部分。

回答by Matthew Steven Monkan

public interface IRepository
{
    void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class;
}

public class Repository : DbContext, IRepository
{
    public void Update<T>(T obj, params Expression<Func<T, object>>[] propertiesToUpdate) where T : class
    {
        Set<T>().Attach(obj);
        propertiesToUpdate.ToList().ForEach(p => Entry(obj).Property(p).IsModified = true);
        SaveChanges();
    }
}

回答by Chriss

Depending on your use case, all the above solutions apply. This is how i usually do it however :

根据您的用例,上述所有解决方案都适用。然而,这是我通常的做法:

For server side code (e.g. a batch process) I usually load the entities and work with dynamic proxies. Usually in batch processes you need to load the data anyways at the time the service runs. I try to batch load the data instead of using the find method to save some time. Depending on the process I use optimistic or pessimistic concurrency control (I always use optimistic except for parallel execution scenarios where I need to lock some records with plain sql statements, this is rare though). Depending on the code and scenario the impact can be reduced to almost zero.

对于服务器端代码(例如批处理),我通常加载实体并使用动态代理。通常在批处理过程中,您无论如何都需要在服务运行时加载数据。我尝试批量加载数据而不是使用 find 方法来节省一些时间。根据我使用乐观或悲观并发控制的过程(我总是使用乐观并发控制,除了并行执行场景,我需要用普通的 sql 语句锁定一些记录,但这很少见)。根据代码和场景,影响可以减少到几乎为零。

For client side scenarios, you have a few options

对于客户端场景,您有几个选择

  1. Use view models. The models should have a property UpdateStatus(unmodified-inserted-updated-deleted). It is the responsibility of the client to set the correct value to this column depending on the user actions (insert-update-delete). The server can either query the db for the original values or the client should send the original values to the server along with the changed rows. The server should attach the original values and use the UpdateStatus column for each row to decide how to handle the new values. In this scenario I always use optimistic concurrency. This will only do the insert - update - delete statements and not any selects, but it might need some clever code to walk the graph and update the entities (depends on your scenario - application). A mapper can help but does not handle the CRUD logic

  2. Use a library like breeze.js that hides most of this complexity (as described in 1) and try to fit it to your use case.

  1. 使用视图模型。模型应该有一个属性 UpdateStatus(unmodified-inserted-updated-deleted)。客户端负责根据用户操作(插入-更新-删除)为此列设置正确的值。服务器可以查询数据库以获取原始值,或者客户端应将原始值与更改的行一起发送到服务器。服务器应附加原始值并使用每一行的 UpdateStatus 列来决定如何处理新值。在这种情况下,我总是使用乐观并发。这只会执行插入 - 更新 - 删除语句,而不是任何选择,但它可能需要一些巧妙的代码来遍历图形并更新实体(取决于您的场景 - 应用程序)。映射器可以帮助但不处理 CRUD 逻辑

  2. 使用像微风.js 这样的库来隐藏大部分复杂性(如 1 中所述),并尝试使其适合您的用例。

Hope it helps

希望能帮助到你