简体   繁体   English

实体框架 5 更新记录

[英]Entity Framework 5 Updating a Record

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

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 ;-)如果不是方法一将占上风;-)

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

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.此示例允许您在向用户表和视图添加新字段后,基本上不考虑业务逻辑。

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

I have added an extra update method onto my repository base class that's similar to the update method generated by Scaffolding.我在我的存储库基类中添加了一个额外的更新方法,它类似于 Scaffolding 生成的更新方法。 Instead of setting the entire object to "modified", it sets a set of individual properties.它不是将整个对象设置为“已修改”,而是设置一组单独的属性。 (T is a class generic parameter.) (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.我还没有这样做,因为我不知道如何避免将我的视图模型验证器上的验证消息带入我的域项目。

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

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 Mapper to update the parts of the record you want to change..您还可以从数据库中获取对象,然后使用Auto Mapper 之类的自动映射工具来更新要更改的记录部分。

Depending on your use case, all the above solutions apply.根据您的用例,上述所有解决方案都适用。 This is how i usually do it however :然而,这是我通常的做法:

For server side code (eg 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.我尝试批量加载数据而不是使用 find 方法来节省一些时间。 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).根据我使用乐观或悲观并发控制的过程(我总是使用乐观并发控制,除了并行执行场景,我需要用普通的 sql 语句锁定一些记录,但这种情况很少见)。 Depending on the code and scenario the impact can be reduced to almost zero.根据代码和场景,影响可以减少到几乎为零。

For client side scenarios, you have a few options对于客户端场景,您有几个选择

  1. Use view models.使用视图模型。 The models should have a property UpdateStatus(unmodified-inserted-updated-deleted).模型应该有一个属性 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.服务器应附加原始值并使用每一行的 UpdateStatus 列来决定如何处理新值。 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映射器可以帮助但不处理 CRUD 逻辑

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

Hope it helps希望能帮助到你

There are some really good answers given already, but I wanted to throw in my two cents.已经给出了一些非常好的答案,但我想投入我的两分钱。 Here is a very simple way to convert a view object into a entity.这是将视图对象转换为实体的一种非常简单的方法。 The simple idea is that only the properties that exist in the view model get written to the entity.简单的想法是只有存在于视图模型中的属性才会被写入实体。 This is similar to @Anik Islam Abhi's answer, but has null propagation.这类似于@Anik Islam Abhi 的回答,但具有空传播。

public static T MapVMUpdate<T>(object updatedVM, T original)
{
    PropertyInfo[] originalProps = original.GetType().GetProperties();
    PropertyInfo[] vmProps = updatedVM.GetType().GetProperties();
    foreach (PropertyInfo prop in vmProps)
    {
        PropertyInfo projectProp = originalProps.FirstOrDefault(x => x.Name == prop.Name);
        if (projectProp != null)
        {
            projectProp.SetValue(original, prop.GetValue(updatedVM));
        }
    }
    return original;
}

Pros优点

  • Views don't need to have all the properties of the entity.视图不需要具有实体的所有属性。
  • You never have to update code when you add remove a property to a view.向视图添加删除属性时,您永远不必更新代码。
  • Completely generic完全通用

Cons缺点

  • 2 hits on the database, one to load the original entity, and one to save it. 2 次点击数据库,一次加载原始实体,一次保存它。

To me the simplicity and low maintenance requirements of this approach outweigh the added database call.对我来说,这种方法的简单性和低维护要求超过了增加的数据库调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM