linq 对象透视表C# MVC解决方案

fzsnzjdm  于 2022-12-06  发布在  C#
关注(0)|答案(1)|浏览(168)

In my controller I am returning a db query result (list of objects) defined by my model. I'm displaying this in the view. For example:
| prop1 | prop2 | prop3 | prop4 | prop5 |
| ------------ | ------------ | ------------ | ------------ | ------------ |
| AAA | 1123 | 400 | 35% | $600 |
| BBB | 3444 | 23 | 45% | $235 |
| CCC | 5000 | 55 | 15% | $555 |
| DDD | 2500 | 264 | 70% | $243 |
I would like to pivot this result to look like below:
| prop1 | AAA | BBB | CCC | DDD |
| ------------ | ------------ | ------------ | ------------ | ------------ |
| prop2 | 1123 | 3444 | 5000 | 2500 |
| prop3 | 400 | 23 | 55 | 264 |
| prop4 | 35% | 45% | 15% | 70% |
| prop5 | $600 | $235 | $555 | $243 |
What's the best way to achieve this? I had initially pivoted in the SQL query and had a model representing the bottom result , however it became difficult to manage as I had frequent changes and requests to add new fields calculated from other columns etc.

atmip9wb

atmip9wb1#

请参见下面的示例实现扩展方法,该方法使用反射来透视IEnumerable<T>

    • 示例用法透视表()**
// Test Type
public record MyObj(string Prop1, int Prop2, int Prop3);

// test data
var list = new List<MyObj> {
        new MyObj("AAA", 1123, 400),
        new MyObj("BBB", 2123, 500),
        new MyObj("CCC", 3123, 600) 
    };

Console.Write(list.Pivot());

    • 带列选择器的透视表()用法示例**
  • 示例1 *
Console.Write(list.Pivot( pivotOn: (f)=>f.Prop1));

  • 示例2 *
Console.Write(list.Pivot( pivotOn: (f)=>f.Prop2));

    • 实施**
public static class PivotExtension
{
    public static IEnumerable<ExpandoObject> Pivot<T>(this IEnumerable<T> objs)
    {
        var objArray = objs.ToArray();
        var properties = typeof(T).GetProperties();
        foreach (var property in properties)
        {
            var obj = new ExpandoObject();
            var objDic = (IDictionary<string, object>)obj;
            objDic.Add("Name", property.Name);
            for (int i = 1; i < objs.Count(); i++)
                objDic.Add($"Row_{i.ToString()}", property.GetValue(objArray[i]));
                
            yield return obj;
        }
    }
    public static IEnumerable<ExpandoObject> Pivot<T, TValue>(this IEnumerable<T> objs, Expression<Func<T, TValue>> pivotOn)
    {
        var objArray = objs.ToList();
        var properties = typeof(T).GetProperties();
        var pivotProperty = pivotOn.GetProperty();
        
        //header
        var cnt = 1;
        var pivotHeader = new Dictionary<int, string>{{0, pivotProperty.Name}};
        objArray.ForEach(r=> pivotHeader.Add( cnt++, pivotProperty.GetValue(r).ToString()));
        
        //rotate
        foreach (var property in properties.Where(r=> !pivotHeader.ContainsValue(r.Name)))
        {
            var obj = new ExpandoObject();
            var objDic = (IDictionary<string, object>)obj;
            objDic.Add(pivotHeader[0], property.Name);
            for (int i = 0; i < objs.Count(); i++)
                objDic.Add($"{pivotHeader[i+1]}", property.GetValue(objArray[i]));

            yield return obj;
        }
    }
    
    public static PropertyInfo GetProperty<T, TValue>(this Expression<Func<T, TValue>> expression)
    {
        return GetProperty(expression.Body);
    }
    
    private static PropertyInfo GetProperty(Expression body)
    {
        MemberExpression memberExpression;
        switch (body.NodeType)
        {
            case ExpressionType.Convert:
                memberExpression = (MemberExpression)((UnaryExpression)body).Operand;
                break;
            case ExpressionType.MemberAccess:
                memberExpression = (MemberExpression)body;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }

        return (PropertyInfo)memberExpression.Member;
    }
}

相关问题