【问题标题】:Assigning a value to a property by reference to a lambda expression通过引用 lambda 表达式为属性赋值
【发布时间】:2015-02-22 19:08:35
【问题描述】:

我有 2 个对象(一个 DTO 和一个入口框架对象),如果 DTO 中的相应值不同,我想更新 EF 实体。我设想它会这样做: UpdateIfDifferent(dtoObject, x => x.MailStatus, efObject, r => r.MailStatus); UpdateIfDifferent(dtoObject, x => x.WebStatus, efObject, r => r.WebStatus);

private void UpdateIfDifferent<TOriginal, TOriginalProperty, TUpdated, TUpdatedProperty>
  (TOriginal original, Expression<Func<TOriginal, TOriginalProperty>> originalProperty, 
TUpdated updated, Expression<Func<TUpdated, TUpdatedProperty>> updatedProperty)
  {
      if (!originalProperty.Compile()(original).Equals(updatedProperty.Compile()(updated)))  {
        // how do I assign back to original from updated?
        }
      }
    }

我相信以下应该可以工作,但我不知道如何将值分配回 efObject。谁能帮我解答一下?

【问题讨论】:

  • 你需要从originalProperty.Body得到MemberExpression,然后得到PropertyInfo,然后你可以调用Get\SetValue方法传递你原来\更新的实体
  • Jonathan,我真的相信你不会从介绍这个方法中得到太多帮助。它的签名令人困惑,实现只是一个“如果”和一个赋值。如果我是你,我会重新考虑我的方法并重构我的代码以完全删除此方法。在我看来,单行 if 和赋值而不是方法调用会使代码更简洁。

标签: c# entity-framework lambda expression-trees


【解决方案1】:

你可以这样做:

public static void Main()       
{
    var original = new TestEntity();
    original.Name = "test";

    var dto = new TestDTO();
    dto.FirstName = "New Value";

    UpdateIfDifferent(original, o => o.Name, dto, d => d.FirstName);

    Console.WriteLine(original.Name);
}

private static void UpdateIfDifferent<TOriginal, TOriginalProperty, TUpdated, TUpdatedProperty>
    (TOriginal original, Expression<Func<TOriginal, TOriginalProperty>> originalProperty, 
        TUpdated updated, Expression<Func<TUpdated, TUpdatedProperty>> updatedProperty)
{
  if (!originalProperty.Compile()(original).Equals(updatedProperty.Compile()(updated)))  
  {

        var updatedMember = (updatedProperty.Body as MemberExpression).Member as PropertyInfo;
        var updatedValue = updatedMember.GetValue(updated);

        var member = (originalProperty.Body as MemberExpression).Member as PropertyInfo;
        member.SetValue(original, updatedValue);

    }
}

public class TestEntity
{
    public string Name {get;set;}
}

public class TestDTO
{
    public string FirstName {get;set;}
}

这是 DotNetFiddle 上的一个工作示例 - https://dotnetfiddle.net/k1qLZH

此示例仅用于属性和成员访问。因此,如果您的表达式将具有另一个签名,例如方法、子属性、字段访问,那么此代码将失败。

另一种不需要 lambda 编译但需要一些时间的方法可能是:

private static void UpdateIfDifferent<TOriginal, TOriginalProperty, TUpdated, TUpdatedProperty>
    (TOriginal original, Expression<Func<TOriginal, TOriginalProperty>> originalProperty, 
        TUpdated updated, Expression<Func<TUpdated, TUpdatedProperty>> updatedProperty)
{

    var updatedMember = (updatedProperty.Body as MemberExpression).Member as PropertyInfo;
    var updatedValue = updatedMember.GetValue(updated);

    var originalMember = (originalProperty.Body as MemberExpression).Member as PropertyInfo;
    var originalValue = originalMember.GetValue(original);

    if (!object.Equals(updatedValue, originalValue))
        originalMember.SetValue(original, updatedValue);

}

【讨论】:

  • 虽然这可能有效,但它看起来很难看。具有四个(原文如此!)参数的泛型和一种方法中的反射,看起来只不过是一种代码味道。问题不是解决方案,而是方法的原始签名使它变得如此糟糕。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-01
  • 1970-01-01
  • 2011-11-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多