簡體   English   中英

映射相同類型的兩個對象(不包括某些字段)的最佳方法是什么?

[英]What is the best way to map two objects of the same type excluding some fields?

我以前在這里發布過我的問題但我沒有得到任何答復,原因 - 我猜 - 這太籠統了。 我會盡量簡潔一些。

我有兩個相同類型的對象,我想映射一些屬性並排除其他屬性。 我想要做的是將對象保存在緩存中並稍后獲取它,僅使用具有特定屬性的屬性(字段)。

我看過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 = Map<T>(myCache.Get(Key) as T, viewModel);

我不知道這是否是最好的方法,但它似乎是我找到的唯一方法。
任何建議將不勝感激。

使用直接反射(如示例中所示)將是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);
    }
}

聽起來是一個好的開始,但您正在失去 AutoMapper 的許多好處。 AutoMapper 還可以處理您在此處沒有的嵌套屬性(當您的類包含嵌套映射類時)。

由於 AutoMapper 是一個 oprn 源項目,我建議您使用 AutoMapper 源並在那里實現過濾。 這實際上對其他人也有用。

我覺得你的方法沒問題。 反射對性能有一些影響——值得考慮。

另一種高效且更簡單的方法可能是讓 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.
        );
    }

    ...
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM