如何找到基于与AutoMapper扁平属性的名称源属性 [英] How to find the source property based on the name of a flattened property with AutoMapper

查看:132
本文介绍了如何找到基于与AutoMapper扁平属性的名称源属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用AutoMapper,我想它根据映射(扁平)目标属性的名称,追溯源属性。

I'm using AutoMapper and I'd like it to trace back a source property based on the name of the mapped (flattened) destination property.

这是因为我的MVC控制器具有一个对应的属性的,它需要提供一个服务调用的名称进行排序的目的。该服务需要知道的映射源于属性的名称(和控制器不应该知道),以执行到实际排序的数据仓库适当的呼叫。

This is because my MVC controller has the name of a mapped property that it needs to provide to a service call that for sorting purposes. The service needs to know the name of the property that the mapping originated from (and the controller is not supposed to know it) in order to perform a proper call to the repository that actually sorts the data.

例如:

[Source.Address.Zip code]映射到[Destination.AddressZip code]

[Source.Address.ZipCode] maps to [Destination.AddressZipCode]

然后

跟踪AddressZip code回[Source.Address.Zip code]

Trace "AddressZipCode" back to [Source.Address.ZipCode]

这是一些AutoMapper能为我做什么或做我需要求助于挖掘AutoMapper的映射数据?

Is this something that AutoMapper can do for me or do I need to resort to digging into AutoMapper's mapping data?

更新

吉米·博加德告诉我,这应该是可能的,但没有明显的方式。它需要加载类型地图,通过它。我看着它简单,但似乎我需要访问内部的类型去所需要做反向映射属性映射信息。

Jimmy Bogard told me that this should be possible but not in an obvious manner. It requires loading the type map and going through it. I've looked into it briefly but it seems that I need access to internal types to get to the property mapping information that is required to do reverse mapping.

更新2

我已经决定提供一些更多的细节。

I've decided to provide some more details.

当我加载的类型映射表,我发现有在这两个源值解析器为隐邮编code映射:

When I load up the type map, I find that there are two source value resolvers in it for the implicit ZipCode mapping:

  • AutoMapper.Internal.PropertyGetter ,获取地址。
  • AutoMapper.Internal.PropertyGetter 是可以获得邮编code。
  • a AutoMapper.Internal.PropertyGetter that gets the Address.
  • a AutoMapper.Internal.PropertyGetter that gets the ZipCode.

当我有一个明确的映射(有指定的lambda EX pression),我觉得没有源值解析,但自定义解析:

When I have an explicit mapping (that has a lambda expression specified), I find no source value resolver but a custom resolver:

  • AutoMapper.DelegateBasedResolver<公司,串> ,我想保持我的显式映射的lambda EX pression
  • a AutoMapper.DelegateBasedResolver<Company,string> that I think holds my explicit mapping lambda expression.

不幸的是,这些解析器是内部的,所以我只能通过反射(我真的不想这样做),或者通过改变AutoMapper源$ C ​​$ C访问它们。

Unfortunately these resolvers are internal so I can only access them through reflection (which I really don't want to do) or by changing the AutoMapper source code.

如果我能访问它们,我既可以由通过价值解析器,或通过检查自定义冲突解决行走解决这个问题,虽然我对此表示怀疑,这将导致我回到映射的lambda EX pression,我需要建立在不平属性名(实际上是一系列的属性名称由点分隔)。

If I could access them, I could solve the problem by either walking through the value resolvers or by inspecting the custom resolver although I doubt it that would lead me back to the mapping lambda expression which I need to build the unflattened property name (actually a series of property names separated by dots).

推荐答案

暂时,我写的,可以从一个连接的产业链决定发起产业链一个辅助类。 Ofcourse,这将成为过时的时候AutoMapper得到一个功能,做这种事情。

For the time being, I wrote a helper class that can determine the originating property chain from a concatenated property chain. Ofcourse, this will become obsolete when AutoMapper gets a feature to do this kind of thing.

using System.Globalization;
using System.Reflection;

/// <summary>
///     Resolves concatenated property names back to their originating properties.
/// </summary>
/// <remarks>
///     An example of a concatenated property name is "ProductNameLength" where the originating
///     property would be "Product.Name.Length".
/// </remarks>
public static class ConcatenatedPropertyNameResolver
{
    private static readonly object mappingCacheLock = new object();
    private static readonly Dictionary<MappingCacheKey, string> mappingCache = new Dictionary<MappingCacheKey, string>();

    /// <summary>
    ///     Returns the nested name of the property the specified concatenated property
    ///     originates from.
    /// </summary>
    /// <param name="concatenatedPropertyName">The concatenated property name.</param>
    /// <typeparam name="TSource">The mapping source type.</typeparam>
    /// <typeparam name="TDestination">The mapping destination type.</typeparam>
    /// <returns>
    ///     The nested name of the originating property where each level is separated by a dot.
    /// </returns>
    public static string GetOriginatingPropertyName<TSource, TDestination>(string concatenatedPropertyName)
    {
        if (concatenatedPropertyName == null)
        {
            throw new ArgumentNullException("concatenatedPropertyName");
        }
        else if (concatenatedPropertyName.Length == 0)
        {
            throw new ArgumentException("Cannot be empty.", "concatenatedPropertyName");
        }

        lock (mappingCacheLock)
        {
            MappingCacheKey key = new MappingCacheKey(typeof(TSource), typeof(TDestination), concatenatedPropertyName);

            if (!mappingCache.ContainsKey(key))
            {
                BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public;

                List<string> result = new List<string>();
                Type type = typeof(TSource);

                while (concatenatedPropertyName.Length > 0)
                {
                    IEnumerable<PropertyInfo> properties = type.GetProperties(bindingFlags).Where(
                        n => concatenatedPropertyName.StartsWith(n.Name)).ToList();

                    if (properties.Count() == 1)
                    {
                        string match = properties.First().Name;
                        result.Add(match);
                        concatenatedPropertyName = concatenatedPropertyName.Substring(match.Length);
                        type = type.GetProperty(match, bindingFlags).PropertyType;
                    }
                    else if (properties.Any())
                    {
                        throw new InvalidOperationException(
                            string.Format(
                                CultureInfo.InvariantCulture,
                                "Ambiguous properties found for {0} on type {1}: {2}.",
                                concatenatedPropertyName,
                                typeof(TSource).FullName,
                                string.Join(", ", properties.Select(n => n.Name))));
                    }
                    else
                    {
                        throw new InvalidOperationException(
                            string.Format(
                                CultureInfo.InvariantCulture,
                                "No matching property found for {0} on type {1}.",
                                concatenatedPropertyName,
                                typeof(TSource).FullName));
                    }
                }

                mappingCache.Add(key, string.Join(".", result));
            }

            return mappingCache[key];
        }
    }

    /// <summary>
    ///     A mapping cache key.
    /// </summary>
    private struct MappingCacheKey
    {
        /// <summary>
        ///     The source type.
        /// </summary>
        public Type SourceType;

        /// <summary>
        ///     The destination type the source type maps to. 
        /// </summary>
        public Type DestinationType;

        /// <summary>
        ///     The name of the mapped property.
        /// </summary>
        public string MappedPropertyName;

        /// <summary>
        ///     Initializes a new instance of the <see cref="MappingCacheKey"/> class.
        /// </summary>
        /// <param name="sourceType">The source type.</param>
        /// <param name="destinationType">The destination type the source type maps to.</param>
        /// <param name="mappedPropertyName">The name of the mapped property.</param>
        public MappingCacheKey(Type sourceType, Type destinationType, string mappedPropertyName)
        {
            SourceType = sourceType;
            DestinationType = destinationType;
            MappedPropertyName = mappedPropertyName;
        }
    }
}

下面是一个使用示例:

class TestEntity
{
    public Node Root {get; set;}
}

class Node
{
    public string Leaf {get; set;}
}

class TestFlattenedEntity
{
    public string RootLeaf {get; set;}
}

string result = ConcatenatedPropertyNameResolver.GetOriginatingPropertyName<TestEntity, TestFlattenedEntity>("RootLeaf");

Assert.AreEqual("Root.Leaf", result);

这篇关于如何找到基于与AutoMapper扁平属性的名称源属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆