[英]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.