LINQ : Dynamic select

前端 未结 10 599
北荒
北荒 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:53

    Another approach I've used is a nested ternary operator:

    string col = "Column3";
    var query = table.Select(i => col == "Column1" ? i.Column1 :
                                  col == "Column2" ? i.Column2 :
                                  col == "Column3" ? i.Column3 :
                                  col == "Column4" ? i.Column4 :
                                  null);
    

    The ternary operator requires that each field be the same type, so you'll need to call .ToString() on any non-string columns.

    0 讨论(0)
  • 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<Shipment>().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<T>
    {
    // as a performence consideration I cached already computed type-properties
    private static Dictionary<Type, PropertyInfo[]> _typePropertyInfoMappings = new Dictionary<Type, PropertyInfo[]>();
    private readonly Type _typeOfBaseClass = typeof(T);
    
    private Dictionary<string, List<string>> GetFieldMapping(string fields)
    {
        var selectedFieldsMap = new Dictionary<string, List<string>>();
    
        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<string> { nestedValue });
            }
        }
    
        return selectedFieldsMap;
    }
    
    public Func<T, T> CreateNewStatement(string fields)
    {
        ParameterExpression xParameter = Expression.Parameter(_typeOfBaseClass, "s");
        NewExpression xNew = Expression.New(_typeOfBaseClass);
    
        var selectFields = GetFieldMapping(fields);
    
        var shpNestedPropertyBindings = new List<MemberAssignment>();
        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<Func<T,T>>( xInit, xParameter );
    
        return lambda.Compile();
    }
    
    0 讨论(0)
  • 2020-11-22 05:56

    Using Reflection and Expression bulid can do what you say. Example:

    var list = new List<Data>();
    //bulid a expression tree to create a paramter
    ParameterExpression param = Expression.Parameter(typeof(Data), "d");
    //bulid expression tree:data.Field1
    Expression selector = Expression.Property(param,typeof(Data).GetProperty("Field1"));
    Expression pred = Expression.Lambda(selector, param);
    //bulid expression tree:Select(d=>d.Field1)
    Expression expr = Expression.Call(typeof(Queryable), "Select",
        new Type[] { typeof(Data), typeof(string) },
        Expression.Constant(list.AsQueryable()), pred);
    //create dynamic query
    IQueryable<string> query = list.AsQueryable().Provider.CreateQuery<string>(expr);
    var result=query.ToList();
    
    0 讨论(0)
  • 2020-11-22 05:56

    I writing the method in following line for you can work with nested fields taking advantage of Nicholas Butler and Ali.

    You can use this method for dynamically creating to lambda for pass to select and also works for nested fields. You can also work with IQueryable cases.

        /// <param name="Fields">
        /// Format1: "Field1"
        /// Format2: "Nested1.Field1"
        /// Format3: "Field1:Field1Alias"
        /// </param>
        public static Expression<Func<T, TSelect>> DynamicSelectGenerator<T, TSelect>(params string[] Fields)
        {
            string[] EntityFields = Fields;
            if (Fields == null || Fields.Length == 0)
                // get Properties of the T
                EntityFields = typeof(T).GetProperties().Select(propertyInfo => propertyInfo.Name).ToArray();
    
            // input parameter "x"
            var xParameter = Expression.Parameter(typeof(T), "x");
    
            // new statement "new Data()"
            var xNew = Expression.New(typeof(TSelect));
    
            // create initializers
            var bindings = EntityFields
                .Select(x =>
                {
                    string[] xFieldAlias = x.Split(":");
                    string field = xFieldAlias[0];
    
                    string[] fieldSplit = field.Split(".");
                    if (fieldSplit.Length > 1)
                    {
                        // original value "x.Nested.Field1"
                        Expression exp = xParameter;
                        foreach (string item in fieldSplit)
                            exp = Expression.PropertyOrField(exp, item);
    
                        // property "Field1"
                        PropertyInfo member2 = null;
                        if (xFieldAlias.Length > 1)
                            member2 = typeof(TSelect).GetProperty(xFieldAlias[1]);
                        else
                            member2 = typeof(T).GetProperty(fieldSplit[fieldSplit.Length - 1]);
    
                        // set value "Field1 = x.Nested.Field1"
                        var res = Expression.Bind(member2, exp);
                        return res;
                    }
                    // property "Field1"
                    var mi = typeof(T).GetProperty(field);
                    PropertyInfo member;
                    if (xFieldAlias.Length > 1)
                        member = typeof(TSelect).GetProperty(xFieldAlias[1]);
                    else member = typeof(TSelect).GetProperty(field);
    
                    // original value "x.Field1"
                    var xOriginal = Expression.Property(xParameter, mi);
    
                    // set value "Field1 = x.Field1"
                    return Expression.Bind(member, xOriginal);
                }
            );
    
            // initialization "new Data { Field1 = x.Field1, Field2 = x.Field2 }"
            var xInit = Expression.MemberInit(xNew, bindings);
    
            // expression "x => new Data { Field1 = x.Field1, Field2 = x.Field2 }"
            var lambda = Expression.Lambda<Func<T, TSelect>>(xInit, xParameter);
    
            return lambda;
        }
    

    Usage:

    var s = DynamicSelectGenerator<SalesTeam, SalesTeamSelect>(
                "Name:SalesTeamName",
                "Employee.FullName:SalesTeamExpert"
                );
    
    var res = _context.SalesTeam.Select(s);
    
    public class SalesTeam
    {
        public string Name {get; set; }
    
        public Guid EmployeeId { get; set; }
        public Employee Employee { get; set; }
    }
    public class SalesTeamSelect
    {
        public string SalesTeamName {get; set; }
        public string SalesTeamExpert {get; set; }
    }
    
    0 讨论(0)
提交回复
热议问题