在Asp .net core 中通过属性映射实现动态排序和数据塑形

属性映射服务实现

image-20210826182810872

public class PropertyMappingValue
{
    public IEnumerable<string> DestinationProperties { get; private set; }
    public PropertyMappingValue(IEnumerable<string> destinationProperties)
    {
        DestinationProperties = destinationProperties;
    }
}
public class PropertyMapping<TSource, TDestination> : IPropertyMapping
{
    public Dictionary<string, PropertyMappingValue> _mappingDictionary { get; set; }
    public PropertyMapping(Dictionary<string, PropertyMappingValue> mappingDictionary)
    {
        _mappingDictionary = mappingDictionary;
    }
}
public class PropertyMappingService : IPropertyMappingService
{
    //PropertyMapping
    private Dictionary<string, PropertyMappingValue> _touristRoutePropertyMapping =
        new Dictionary<string, PropertyMappingValue>(StringComparer.OrdinalIgnoreCase)
    {
        {"Id", new PropertyMappingValue(new List<string>(){"Id"}) },
        {"Title", new PropertyMappingValue(new List<string>(){"Title"}) },
        {"Rating", new PropertyMappingValue(new List<string>(){"Rating"}) },
        {"OriginalPrice", new PropertyMappingValue(new List<string>(){"OriginalPrice"})},
    };

    //PropertyMappings
    private IList<IPropertyMapping> _propertyMappings
        = new List<IPropertyMapping>();

    public PropertyMappingService()
    {
        _propertyMappings.Add(
            new PropertyMapping<TouristRouteDto, TouristRoute>		  (_touristRoutePropertyMapping)
        );
    }

    //GetPropertyMapping
    public Dictionary<string, PropertyMappingValue>
        GetPropertyMapping<TSource, TDestination>()
    {
        var matchingMapping =
            _propertyMappings.OfType<PropertyMapping<TSource, TDestination>>();

        if (matchingMapping.Count() == 1)
        {
            return matchingMapping.First()._mappingDictionary;
        }
        throw new Exception($"Cannot find exact property mapping instance for <{typeof(TSource)}, {typeof(TDestination)}>");
    }
	
    //IsMappingExists
    public bool IsMappingExists<TSource, TDestination>(string fields)
    {
        var propertyMapping = GetPropertyMapping<TSource, TDestination>();

        if (string.IsNullOrWhiteSpace(fields))
        {
            return true;
        }

        var fieldsAfterSplit = fields.Split(",");

        foreach (var field in fieldsAfterSplit)
        {
            var trimmedField = field.Trim();
            var indexOfFirstSpace = trimmedField.IndexOf(" ");
            var propertyName = indexOfFirstSpace == -1 ?
                trimmedField : trimmedField.Remove(indexOfFirstSpace);

            if (!propertyMapping.ContainsKey(propertyName))
            {
                return false;
        	}
        }
        return true;
    }

    //IsPropertiesExists
	public bool IsPropertiesExists<T>(string fields)
	{
	    if (string.IsNullOrWhiteSpace(fields))
	    {
	        return true;
        }
	    var fieldsAfterSplit = fields.Split(',');
	    foreach (var field in fieldsAfterSplit)
	    {
	        var propertyName = field.Trim();
	        var propertyInfo = typeof(T)
                .GetProperty(
                    propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance
                );
                
            if (propertyInfo == null)
            {
                return false;
            }
        }
        return true;
    }
}

注入服务image-20210826204944633

动态排序

在Helper中扩展IQueryable封装了ApplySort方法实现动态排序

public static class IQueryableExtensions
{
    public static IQueryable<T> ApplySort<T>(
        this IQueryable<T> source,
        string orderBy,
        Dictionary<string, PropertyMappingValue> mappingDictionary
    )
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }

        if (mappingDictionary == null)
        {
            throw new ArgumentNullException("mappingDictionary");
        }

        if (string.IsNullOrWhiteSpace(orderBy))
        {
             return source;
        }

        var orderByString = string.Empty;

        var orderByAfterSplit = orderBy.Split(',');

        foreach (var order in orderByAfterSplit)
        {
            var trimmedOrder = order.Trim();

            var orderDescending = trimmedOrder.EndsWith(" desc");

            var indexOfFirstSpace = trimmedOrder.IndexOf(" ");
            var propertyName = indexOfFirstSpace == -1
                ? trimmedOrder
                : trimmedOrder.Remove(indexOfFirstSpace);

            if (!mappingDictionary.ContainsKey(propertyName))
            {
                throw new ArgumentException($"Key mapping for {propertyName} is missing");
            }

            var propertyMappingValue = mappingDictionary[propertyName];
            if (propertyMappingValue == null)
            {
                throw new ArgumentNullException("propertyMappingValue");
            }

            foreach (var destinationProperty in
                propertyMappingValue.DestinationProperties)
            {
                orderByString = orderByString +
                    (string.IsNullOrWhiteSpace(orderByString) ? string.Empty : ", ")
                    + destinationProperty
                    + (orderDescending ? " descending" : " ascending");
            }
        }
        return source.OrderBy(orderByString);
    }
}

在repository中使用image-20210826210046091

数据塑形

安装Nuget包image-20210826215148810

在Helper中封装了IEnumerableExtensions、ObjectExtensions,实现对数据的动态塑形。

public static class IEnumerableExtensions
{
    public static IEnumerable<ExpandoObject> ShapeData<TSource>(
        this IEnumerable<TSource> source,
        string fields
    )
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        var expandoObjectList = new List<ExpandoObject>();

        //避免在列表中遍历数据,创建一个属性信息列表
        var propertyInfoList = new List<PropertyInfo>();

        if (string.IsNullOrWhiteSpace(fields))
        {
            // 希望返回动态类型对象ExpandoObject所有的属性
            var propertyInfos = typeof(TSource)
              	.GetProperties(BindingFlags.IgnoreCase
                | BindingFlags.Public | BindingFlags.Instance);

        	propertyInfoList.AddRange(propertyInfos);
        }
        else
       	{
            //逗号来分隔字段字符串
            var fieldsAfterSplit = fields.Split(',');

            foreach (var filed in fieldsAfterSplit)
            {
                // 去掉首尾多余的空格,获得属性名称
                var propertyName = filed.Trim();

                var propertyInfo = typeof(TSource)
                    .GetProperty(propertyName, BindingFlags.IgnoreCase
                | BindingFlags.Public | BindingFlags.Instance);

                if (propertyInfo == null)
                {
                    throw new Exception($"属性 {propertyName} 找不到" +
                        $" {typeof(TSource)}");
                }

                propertyInfoList.Add(propertyInfo);
            }
        }

        foreach (TSource sourceObject in source)
        {
            // 创建动态类型对象, 创建数据塑性对象
            var dataShapedObject = new ExpandoObject
            foreach (var propertyInfo in propertyInfoList)
            {
                //获得对应属性的真实数据
                var propertyValue = propertyInfo.GetValue(sourceObjec
                ((IDictionary<string, object>)dataShapedObject)
                    .Add(propertyInfo.Name, propertyValue);
            
            expandoObjectList.Add(dataShapedObject);
        }

        return expandoObjectList;
    }
}
public static class ObjectExtensions
{
    public static ExpandoObject ShapeData<TSource>(
        this TSource source,
        string fields
    )
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        var dataShapedObject = new ExpandoObject();

        if (string.IsNullOrWhiteSpace(fields))
        {
            var propertyInfos = typeof(TSource)
                    .GetProperties(BindingFlags.IgnoreCase |
                    BindingFlags.Public | BindingFlags.Instance);

            foreach (var propertyInfo in propertyInfos)
            {
                var propertyValue = propertyInfo.GetValue(source);

                ((IDictionary<string, object>)dataShapedObject)
                    .Add(propertyInfo.Name, propertyValue);
            }

            return dataShapedObject;
        }

        var fieldsAfterSplit = fields.Split(',');

        foreach (var field in fieldsAfterSplit)
        {
            var propertyName = field.Trim();

            var propertyInfo = typeof(TSource)
                .GetProperty(propertyName,
                BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

            if (propertyInfo == null)
            {
                throw new Exception($"Property {propertyName} wasn't found " +
                    $"on {typeof(TSource)}");
            }

            var propertyValue = propertyInfo.GetValue(source);

            ((IDictionary<string, object>)dataShapedObject)
                .Add(propertyInfo.Name, propertyValue);
        }

        return dataShapedObject;
    }
}

在控制器的Action中使用

image-20210826215636707image-20210826215714384

posted @ 2021-08-26 22:00  bleso  阅读(18)  评论(0编辑  收藏  举报