映射两个相同类型的对象(不包括某些字段)的最佳方法是什么?

时间:2011-03-29 10:25:22

标签: c# reflection mapping

我之前发布过我的问题here,但我没有得到任何回复原因 - 我猜 - 它太通用了。我会尽量简洁一点。

我有两个相同类型的对象,我想映射一些属性并排除其他属性。我要做的是将对象保存在缓存中并稍后使用具有特定属性的属性(字段)获取它。

我看过 Automapper ,但我找不到任何适合我的东西,所以我想要实现自己的系统。
我创建了一个属性:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class FilterFieldAttribute: Attribute
{
} 

并在我需要包含的字段上用它装饰了一个类:

public class OrdersViewModel : BaseViewModel
{
...

    [FilterField]
    [DisplayName("Order Number:")]
    public string OrderNumber { get; set; }

    [FilterField]
    [DisplayName("From date:")]
    public DateTime FromDate { get; set; }

    [FilterField]
    [DisplayName("To date:")]
    public DateTime ToDate { get; set; }

    [DisplayName("Status:")]
    public int Status { get; set; }

    ...
} 

现在,我已经实现了一个负责映射的函数:

    private T Map<T>(T Source, T Destination) where T : BaseViewModel
    {
        if (Source == null)
        {
            return (Source);
        }

        FilterFieldAttribute filterAttribute;

        foreach (PropertyInfo propInfo in typeof(T).GetProperties())
        {
            foreach (FilterFieldAttribute attr in propInfo.GetCustomAttributes(typeof(FilterFieldAttribute), false))
            {
                filterAttribute = attr as FilterFieldAttribute;
                if (filterAttribute != null)
                {
                    var value = propInfo.GetValue(Source, null);
                    propInfo.SetValue(Destination, value, null);
                }
            }
        }
        return (Destination);
    }

现在,当我需要从缓存中获取viewmodel并仅填充标记有该属性的属性时,我的代码如下所示:

viewModel = Map<T>(myCache.Get(Key) as T, viewModel);

我不知道这是否是最好的,但这似乎是我找到的唯一方法 任何建议都将不胜感激。

3 个答案:

答案 0 :(得分:2)

使用直接反射(如示例中)将是 sloooow ;给出一个更完整的答案是棘手的,因为它取决于你是想要浅层克隆还是深层克隆子对象。无论哪种方式,您都应该期望这涉及一些元编程和缓存 - 使用ILGenerator或Expression。

但是,对于惰性选项,序列化可能很有用。许多序列化程序允许您使用属性来包含/排除特定项目;通过序列化到内存流,倒带(.Position=0)和反序列化你应该得到你所选成员的深层副本。

以下是使用Expression的示例:

using System;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class FilterFieldAttribute: Attribute
{
    public static T Clone<T>(T obj) where T : class, new()
    {
        return Cache<T>.clone(obj);
    }

    private static class Cache<T> where T : class, new()
    {
        public static readonly Func<T,T> clone;
        static Cache()
        {
            var param = Expression.Parameter(typeof(T), "source");
            var members = from prop in typeof(T).GetProperties()
                          where Attribute.IsDefined(prop, typeof(FilterFieldAttribute))
                          select Expression.Bind(prop, Expression.Property(param, prop));

            var newObj = Expression.MemberInit(Expression.New(typeof(T)), members);
            clone = Expression.Lambda<Func<T,T>>(newObj, param).Compile();
        }
    }
} 

public class OrdersViewModel 
{
    [FilterField]
    [DisplayName("Order Number:")]
    public string OrderNumber { get; set; }

    [FilterField]
    [DisplayName("From date:")]
    public DateTime FromDate { get; set; }

    [FilterField]
    [DisplayName("To date:")]
    public DateTime ToDate { get; set; }

    [DisplayName("Status:")]
    public int Status { get; set; }

    static void Main()
    {
        var foo = new OrdersViewModel { OrderNumber = "abc", FromDate = DateTime.Now,
            ToDate = DateTime.Now, Status = 1};
        var bar = FilterFieldAttribute.Clone(foo);
    }
}

答案 1 :(得分:1)

听起来像是一个好的开始,但你正在失去AutoMapper的这么多好处。 AutoMapper还可以处理您在此处没有的嵌套属性(当您的类包含嵌套的映射类时)。

由于AutoMapper是一个oprn源项目,我建议您使用AutoMapper源并在那里实现过滤。事实上,这对其他人来说也是有益的。

答案 2 :(得分:0)

我认为您的方法还可以。反思有一些性能影响 - 值得考虑。

一种替代的,高效的,更简单的方法可能是让BaseViewModel定义一个抽象方法:

public abstract BaseViewModel ToCacheVersion();

可用于将子类转换为正确的类型。每个子类都会处理自己的映射:

public class ViewModelX
{
    public ViewModelX(string name, string description)
    {
        Name = name;
        Description = description;
    }

    ...

    public override BaseViewModel ToCacheVersion()
    {
        return new ViewModelX(
            Name, // Include the name.
            null  // Ignore the description.
        );
    }

    ...
}
相关问题