克隆不同对象属性的最佳方法

4
我有一个MVC3应用程序,需要将视图模型与数据库模型同步。我发现自己编写了太多的代码来在不同的对象之间复制属性。我尽可能避免这种情况,只需对数据模型进行子类化即可,但有时候发现这种做法过于受限。
我开发了一些扩展方法Object来支持浅层克隆具有相似名称的属性,并且效果非常好。然而,我想知道是否有更有效的方法来实现相同的功能。因此,我认为这是请求同行审查并提供改进此代码的选项。
更新: 我发现最好明确处理相关表格。测试IsVirtual可以防止关系在克隆期间被意外影响。请参见更新的CloneMatching方法。其他方法明确说明要更新或排除哪些属性。
public static class CustomExtensions
{
   public static T CloneMatching<T, S>(this T target, S source)
       where T : class
       where S : class
    {
        if (source == null)
        {
            return target;
        }
        Type sourceType = typeof(S);
        Type targetType = typeof(T);
        BindingFlags flags = BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance;

        PropertyInfo[] properties = sourceType.GetProperties();
        foreach (PropertyInfo sPI in properties)
        {
            PropertyInfo tPI = targetType.GetProperty(sPI.Name,flags);
            if (tPI != null && tPI.PropertyType.IsAssignableFrom(sPI.PropertyType) && !tPI.PropertyType.IsVirtual)
            {
                tPI.SetValue(target, sPI.GetValue(source, null), null);
            }
        }
        return target;
    }

    public static T CloneProperties<T, S>(this T target, S source, string[] propertyNames)
       where T : class
       where S : class
    {
        if (source == null)
        {
            return target;
        }
        Type sourceType = typeof(S);
        Type targetType = typeof(T);
        BindingFlags flags = BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance;

        PropertyInfo[] properties = sourceType.GetProperties();
        foreach (PropertyInfo sPI in properties)
        {
            if (propertyNames.Contains(sPI.Name))
            {
                PropertyInfo tPI = targetType.GetProperty(sPI.Name, flags);
                if (tPI != null && tPI.PropertyType.IsAssignableFrom(sPI.PropertyType))
                {
                    tPI.SetValue(target, sPI.GetValue(source, null), null);
                }
            }
        }
        return target;
    }
    public static T CloneExcept<T, S>(this T target, S source, string[] propertyNames)
       where T : class
       where S : class
    {
        if (source == null)
        {
            return target;
        }
        Type sourceType = typeof(S);
        Type targetType = typeof(T);
        BindingFlags flags = BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance;

        PropertyInfo[] properties = sourceType.GetProperties();
        foreach (PropertyInfo sPI in properties)
        {
            if (!propertyNames.Contains(sPI.Name))
            {
                PropertyInfo tPI = targetType.GetProperty(sPI.Name, flags);
                if (tPI != null && tPI.PropertyType.IsAssignableFrom(sPI.PropertyType))
                {
                    tPI.SetValue(target, sPI.GetValue(source, null), null);
                }
            }
        }
        return target;
    }
}

这是一个示例,展示了如何将视图模型映射到数据模型:
```

以下是我如何使用它来将视图模型映射到数据模型的示例。

```
DataSession.Quote.CloneProperties(viewModel,
                new[] {"PaymentType","CardHolder","CardHolderZip","CardNumber","CardExp","CVC",
                          "AccountHolder","AccountHolderZip","ABA","Account",
                          "AccuracyAgreement","PrivacyTermsAgreement","ElectronicSignatureAgreement"});

3
AutoMapper或类似的工具不会帮助你解决这个问题吗? http://automapper.org - Quinton Bernhardt
这是我们用来解决OP情况的方法。 +1 - Casper Leon Nielsen
那肯定能做到我想要的。性能如何?我浏览了一下它的代码,似乎有点过度设计了,超出了我所需的范围。 - B2K
@quinton 看了Automapper的文档,它似乎没有一种干净的方法可以克隆大对象上的特定属性。我已经修改了上面的问题来展示这一点。 - B2K
1
我不是AutoMapper的专家。这是一篇旧文章,也许可以提供一些帮助,否则可能需要另外发一个问题。https://dev59.com/3nNA5IYBdhLWcg3wdthd#6474397 - Quinton Bernhardt
1个回答

1
你可以考虑使用 Object.MemberwiseClone 方法。

MemberwiseClone 方法通过创建一个新对象并将当前对象的非静态字段复制到新对象中来创建浅表副本。(docs.microsoft)

该方法是受保护的。这意味着您必须在您的类中实现 Clone 方法。为了使事情更加高级,您还可以向您的类添加 ICloneable
class MyClass: ICloneable
{
    // all your code
    MyClass Clone()
    {
        return (MyClass)this.MemberwiseClone();
    }
}

这将返回与原始对象相同类型的对象。我想要的是在不同类型的对象之间克隆具有相同名称的属性。 - B2K
请考虑这个页面,其中一个视图模型封装了人员、车辆和驾驶历史信息。在后端,它们被拆分成单独的实体,但呈现给用户的是一个单一表格。https://demoagent.com/applicant - B2K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接