简体   繁体   English

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

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

I've previously post my question here but I didn't get any reply cause - I guess - it's too generic.我以前在这里发布过我的问题但我没有得到任何答复,原因 - 我猜 - 这太笼统了。 I'll try to be more concise.我会尽量简洁一些。

I've got two object of the same type and I want to map some of the properties and exclude others.我有两个相同类型的对象,我想映射一些属性并排除其他属性。 What I am trying to do is save an object in a cache and fetch it later, using only the properties (fields) with a specific attribute.我想要做的是将对象保存在缓存中并稍后获取它,仅使用具有特定属性的属性(字段)。

I've had a look at Automapper but I haven't found anything which seems to be suitable for me so I've thought to implement my own system.我看过Automapper,但没有找到任何适合我的东西,所以我想实现我自己的系统。
I've created an attribute:我创建了一个属性:

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

and decorated a class with it on the fields I need to include:并在我需要包括的字段上用它装饰一个类:

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; }

    ...
} 

Now, I've implemented a function which is responsible for the mapping:现在,我已经实现了一个负责映射的函数:

    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);
    }

Now, when I need to fetch my viewmodel from the cache and fill only the properties marked with the attribute my code looks like this:现在,当我需要从缓存中获取我的视图模型并仅填充标有该属性的属性时,我的代码如下所示:

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

I don't know if this is the best to do it but it seems the only way I have found.我不知道这是否是最好的方法,但它似乎是我找到的唯一方法。
Any suggestion would be appreciated.任何建议将不胜感激。

Using direct reflection (as in the example) will be sloooow ;使用直接反射(如示例中所示)将是sloooow giving a more complete answer is tricky, as it depends on whether you want a shallow clone or deep clone of sub-objects.给出更完整的答案很棘手,因为这取决于您想要子对象的浅克隆还是深克隆。 Either way, you should expect this to involve some meta-programming and caching - using either ILGenerator or Expression.无论哪种方式,您都应该期望这涉及一些元编程和缓存 - 使用 ILGenerator 或 Expression。

However, for a lazy option, serialization might be useful.但是,对于惰性选项,序列化可能很有用。 A number of serializers allow you to use attributes to include/exclude particular items;许多序列化程序允许您使用属性来包含/排除特定项目; by serializing to a memory-stream, rewinding ( .Position=0 ) and deserializing you should get a deep copy of just your chosen members.通过序列化到内存流、倒带 ( .Position=0 ) 和反序列化,您应该只获得所选成员的深层副本。

Here's an example using Expression :下面是一个使用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);
    }
}

Sounds like a good start but you are loosing so much of the benefits of AutoMapper.听起来是一个好的开始,但您正在失去 AutoMapper 的许多好处。 AutoMapper can also take care of the nested properties (when your class contains nested mapped classes) which you do not have here. AutoMapper 还可以处理您在此处没有的嵌套属性(当您的类包含嵌套映射类时)。

Since AutoMapper is an oprn source project, I suggest you take AutoMapper source and implement filtering there.由于 AutoMapper 是一个 oprn 源项目,我建议您使用 AutoMapper 源并在那里实现过滤。 This could be in fact useful for others too.这实际上对其他人也有用。

I think your approach is ok.我觉得你的方法没问题。 Reflection has some performance implications - which is worth considering.反射对性能有一些影响——值得考虑。

An alternative, performant, and simpler approach might be to have the BaseViewModel define an abstract method:另一种高效且更简单的方法可能是让 BaseViewModel 定义一个抽象方法:

public abstract BaseViewModel ToCacheVersion();

Which can be used to convert the subclass to the correct type.可用于将子类转换为正确的类型。 Each subclass would take care of its own mapping:每个子类都会处理自己的映射:

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.

相关问题 Automapper - 映射两个具有相同类型的对象 - Automapper - map two objects that have the same type 划分两个TimeSpan对象的最佳方法是什么? - What is the best way to divide two TimeSpan objects? 在 MVC 中仅更新对象的某些字段的最佳方法是什么? - What is the best way to update only some fields of an object in MVC? 当某些 $type 未知时,使用 TypeNameHandling 反序列化一组对象的最佳方法 - Best way to deserialize set of objects with TypeNameHandling when some $type are unknown 将sql JOIN结果映射到对象的最佳方法是什么? - What is the best way to map sql JOIN results to objects? 比较 c# 中两个对象的最佳方法是什么 - What is the best way to compare two objects in c# 同一台机器上的两个程序相互通信的最佳方法是什么 - What is the best way for two programs on the same machine to communicate with each other 在 LINQ 中组合两个相同类型对象的数组的更好方法 - Better way to combine an array of two objects of the same type in LINQ 反射是比较相同类型的两个对象的更好方法吗? - Is reflection the better way to compare two objects of the same type? 基于相同类型的对象列表从数据库中获取多个对象的最佳方法 - Best way to fetch multiple objects from database, based on a list of objects of the same type
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM