LINQ : Dynamic select

前端 未结 10 725
北荒
北荒 2020-11-22 05:26

Consider we have this class :

    public  class Data
{
    public string Field1 { get; set; }
    public string Field2 { get; set; }
    public string Field         


        
10条回答
  •  自闭症患者
    2020-11-22 05:55

    I have generate my own class for same purpose of usage.

    github gist : https://gist.github.com/mstrYoda/663789375b0df23e2662a53bebaf2c7c

    It generates dynamic select lambda for given string and also support for two level nested properties.

    Example of usage is :

    class Shipment {
       // other fields...
       public Address Sender;
       public Address Recipient;
    }
    
    class Address {
        public string AddressText;
        public string CityName;
        public string CityId;
    }
    
    // in the service method
    var shipmentDtos = _context.Shipments.Where(s => request.ShipmentIdList.Contains(s.Id))
                    .Select(new SelectLambdaBuilder().CreateNewStatement(request.Fields)) // request.Fields = "Sender.CityName,Sender.CityId"
                    .ToList();
    

    It compiles the lambda as below:

    s => new Shipment {
        Sender = new Address {
            CityId = s.Sender.CityId,
            CityName = s.Sender.CityName
        }
    }
    

    You can also find my quesion and answer here :c# - Dynamically generate linq select with nested properties

    public class SelectLambdaBuilder
    {
    // as a performence consideration I cached already computed type-properties
    private static Dictionary _typePropertyInfoMappings = new Dictionary();
    private readonly Type _typeOfBaseClass = typeof(T);
    
    private Dictionary> GetFieldMapping(string fields)
    {
        var selectedFieldsMap = new Dictionary>();
    
        foreach (var s in fields.Split(','))
        {
            var nestedFields = s.Split('.').Select(f => f.Trim()).ToArray();
            var nestedValue = nestedFields.Length > 1 ? nestedFields[1] : null;
    
            if (selectedFieldsMap.Keys.Any(key => key == nestedFields[0]))
            {
                selectedFieldsMap[nestedFields[0]].Add(nestedValue);
            }
            else
            {
                selectedFieldsMap.Add(nestedFields[0], new List { nestedValue });
            }
        }
    
        return selectedFieldsMap;
    }
    
    public Func CreateNewStatement(string fields)
    {
        ParameterExpression xParameter = Expression.Parameter(_typeOfBaseClass, "s");
        NewExpression xNew = Expression.New(_typeOfBaseClass);
    
        var selectFields = GetFieldMapping(fields);
    
        var shpNestedPropertyBindings = new List();
        foreach (var keyValuePair in selectFields)
        {
            PropertyInfo[] propertyInfos;
            if (!_typePropertyInfoMappings.TryGetValue(_typeOfBaseClass, out propertyInfos))
            {
                var properties = _typeOfBaseClass.GetProperties();
                propertyInfos = properties;
                _typePropertyInfoMappings.Add(_typeOfBaseClass, properties);
            }
    
            var propertyType = propertyInfos
                .FirstOrDefault(p => p.Name.ToLowerInvariant().Equals(keyValuePair.Key.ToLowerInvariant()))
                .PropertyType;
    
            if (propertyType.IsClass)
            {
                PropertyInfo objClassPropInfo = _typeOfBaseClass.GetProperty(keyValuePair.Key);
                MemberExpression objNestedMemberExpression = Expression.Property(xParameter, objClassPropInfo);
    
                NewExpression innerObjNew = Expression.New(propertyType);
    
                var nestedBindings = keyValuePair.Value.Select(v =>
                {
                    PropertyInfo nestedObjPropInfo = propertyType.GetProperty(v);
    
                    MemberExpression nestedOrigin2 = Expression.Property(objNestedMemberExpression, nestedObjPropInfo);
                    var binding2 = Expression.Bind(nestedObjPropInfo, nestedOrigin2);
    
                    return binding2;
                });
    
                MemberInitExpression nestedInit = Expression.MemberInit(innerObjNew, nestedBindings);
                shpNestedPropertyBindings.Add(Expression.Bind(objClassPropInfo, nestedInit));
            }
            else
            {
                Expression mbr = xParameter;
                mbr = Expression.PropertyOrField(mbr, keyValuePair.Key);
    
                PropertyInfo mi = _typeOfBaseClass.GetProperty( ((MemberExpression)mbr).Member.Name );
    
                var xOriginal = Expression.Property(xParameter, mi);
    
                shpNestedPropertyBindings.Add(Expression.Bind(mi, xOriginal));
            }
        }
    
        var xInit = Expression.MemberInit(xNew, shpNestedPropertyBindings);
        var lambda = Expression.Lambda>( xInit, xParameter );
    
        return lambda.Compile();
    }
    

提交回复
热议问题