【问题标题】:ASP.NET How to update a record without manually setting each property?ASP.NET 如何在不手动设置每个属性的情况下更新记录?
【发布时间】:2017-07-13 10:47:49
【问题描述】:

如果我有一个对象的 100 个属性发生了变化怎么办?

这对代码来说会很糟糕,并且容易丢失一些属性。

是否有与手动更新记录不同的方式?

ApplicationUser CurrentUser = db.Users.FirstOrDefault(x => x.Id == model.Id);

CurrentUser.FirstName = model.FirstName;
CurrentUser.LastName = model.LastName;
CurrentUser.IsRSM = model.IsRSM;
CurrentUser.PhoneNumber = model.PhoneNumber;
CurrentUser.Email = model.Email;
CurrentUser.UserName = model.Email;
CurrentUser.Region = model.Region;
CurrentUser.Active = model.Active;

db.Entry(CurrentUser).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();

【问题讨论】:

  • 您可以使用反射迭代属性,或使用 AutoMapper 为您完成。
  • 看看 AutoMapper,这可能是最好的选择。
  • 如果我有一个对象的 100 个属性发生了变化怎么办?这将是可怕的代码责备在一个类中创建这些属性的开发人员;)

标签: c# asp.net-mvc api datacontext


【解决方案1】:

根据评论,您可以通过以下两种方式以编程方式执行此操作:

反射

var CurrentUser = db.Users.FirstOrDefault(x => x.Id == model.Id);

PropertyInfo[] sourceProps = model.GetType()
    .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);

PropertyInfo[] targetProps = CurrentUser.GetType()
    .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty);

foreach (var prop in sourceProps)
{
    var targetProp = targetProps.FirstOrDefault(x => x.Name == prop.Name);
    if (targetProp != null)
        targetProp.SetValue(CurrentUser, prop.GetValue(model));
}

db.Entry(CurrentUser).State = EntityState.Modified;
db.SaveChanges();

自动映射器

首先在应用启动时创建地图:

cfg.CreateMap<ApplicationUserViewModel, ApplicationUser>();

然后执行地图:

_mapper.Map<ApplicationUserViewModel, ApplicationUser>(model, CurrentUser)

【讨论】:

  • 请注意,如果属性名称始终相同(或至少大部分相同),任何一种解决方案都只会对您有很大帮助。对于每个具有不同名称的属性映射对,您仍然需要手动设置映射。 (这不是 Chris 回答的缺点,而是无法自动猜测目标属性名称的逻辑后果)
【解决方案2】:

你可以这样做

ApplicationUser CurrentUser = db.Users.FirstOrDefault(x => x.Id == model.Id);
Context.Entry(CurrentUser).CurrentValues.SetValues(ObjectBeingPassed);

在您的方法中,您需要期待您的帖子中出现的记录,该记录将包含每个属性 (ObjectBeingPassed)。

【讨论】:

    【解决方案3】:

    还有第三种方法

    • 创建一个映射方法,您可以在其中手动映射属性。 (没有“昂贵”的反思)
    • 为该映射方法创建单元测试,您可以在其中验证所有属性设置是否正确且没有遗漏。

    通过测试,您将始终确保没有任何属性遗漏或设置为用于它们的错误值。
    通过测试,您可以使用 Automapper 的反射重构您的方法,您将始终“检查”属性映射是否正确。

    【讨论】:

    • "创建一个您手动映射属性的映射方法" 您的第一步正是 OP 试图避免的。此外,由于 OP 担心忘记属性;是什么让您认为他不会担心在单元测试中忘记某个属性?
    • @Flater,当您想要添加或更新(重命名、更改类型等)属性时,您首先将其添加到测试中。如果测试失败,您将更改映射方法,直到测试通过。这样做你不会忘记任何属性。当您将用户输入映射到实体时,主要 OP 的关注点是维护 - 一段时间后,您将遇到不应映射到实体的模型属性的情况。或者它们应该在映射之前被修改/规范化......
    • "如果测试失败,您将更改映射方法,直到测试通过。"编写一个正确失败的测试本质上意味着您必须测试所有属性是否已正确设置。正如我所提到的,OP 发布了这个问题特别是因为他担心忘记设置所有属性。您只是将问题从创建原始映射转移到创建单元测试;但问题仍然没有改变,也没有解决。此外,nowhere 在问题(也不是 cmets)中,OP 没有提到他的主要关注点是维护现有代码。
    • 您只是将问题从创建原始映射转移到创建单元测试;但问题仍然没有改变和未解决 - OP 担心在映射过程中忘记某些属性。如果缺少某些属性,单元测试会通知您。是的——唯一的缺点是你需要编写“正确”的单元测试——但你只做一次。
    • 问题仍然是 OP 害怕忘记属性。不管是a.Field = b.Field; 还是Assert.AreEqual(a.Field, b.Field) 都无所谓。任何一个都容易出现相同的开发人员错误。如果 OP 在同一个代码库中多次编写相同的映射(对于相同的类),我同意您的回答(因此可能会弄乱许多映射中的 one)。但这不是目前所说的问题的一部分。
    猜你喜欢
    • 2014-11-09
    • 2010-09-17
    • 2016-06-10
    • 2023-03-20
    • 2018-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-27
    相关资源
    最近更新 更多