0

I am trying to create a dynamic select builder that includes also nested class. Basically I have entity class DB then DTO like in the following classes

   /// <summary>
    /// Base entity class
    /// </summary>
    public abstract class BaseEntity
    { }

    /// <summary>
    /// EF Schema entity
    /// </summary>
    public class AMOS_AMOSUSER : BaseEntity
    {
        public decimal USERID { get; set; }

        public decimal? SUPERIORID { get; set; }

        public decimal? EMPLOYEEID { get; set; }

        public virtual AMOS_AMOSUSER SUPERIOR { get; set; }

        public string LOGINID { get; set; }

        public string NAME { get; set; }

        public string COMMENT1 { get; set; }

        public decimal ACCOUNTDISABLED { get; set; }

        public DateTime? LASTLOGIN { get; set; }
    }

    /// <summary>
    /// DTO base for all dtos
    /// </summary>
    public abstract class DTO_BASE
    {
    }

    /// <summary>
    /// DTO amos user base
    /// </summary>
    public class DTO_AMOSUSER_BASE : DTO_BASE
    {
        public decimal USERID { get; set; }
        public string LOGINID { get; set; }

        public string NAME { get; set; }
    }

    /// <summary>
    /// DTO AMOS User
    /// </summary>
    public class DTO_AMOSUSER : DTO_AMOSUSER_BASE
    {
        public decimal? SUPERIORID { get; set; }

        public virtual DTO_AMOSUSER_BASE SUPERIOR { get; set; }
    }

Here below code for testing and creating dynamic select.

    public static class TestDynamicSelect
    {

        public static void TestDynamicSelectDTO()
        {
            List<AMOS_AMOSUSER> users = new List<AMOS_AMOSUSER>();
            PopulateUsers(users);

            var q = users.AsQueryable().Select(DynamicSelectGenerator2<AMOS_AMOSUSER, DTO_AMOSUSER>(typeof(DTO_AMOSUSER).GetProperties().Select(p => p.Name)));

            var cc = q.ToArray();

            foreach (var item in cc)
            {
                Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(item));
            }
            Console.ReadLine();
        }

        public static Expression<Func<T, TSelect>> DynamicSelectGenerator1<T, TSelect>(string fields)
        {
            return DynamicSelectGenerator2<T, TSelect>(fields.Split(','));
        }

        /// <param name="fields">
        /// Format1: "Field1"
        /// Format2: "Nested1.Field1"
        /// Format3: "Field1:Field1Alias"
        /// </param>
        public static Expression<Func<T, TSelect>> DynamicSelectGenerator2<T, TSelect>(IEnumerable<string> fields)
        {
            string[] EntityFields;
            if (fields == null || fields.Count() == 0)
                // get Properties of the T
                EntityFields = typeof(T).GetProperties().Select(propertyInfo => propertyInfo.Name).ToArray();
            else
                EntityFields = fields.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"
                    if (member != null)
                    {
                        if (mi != null)
                        {
                            if (typeof(BaseEntity).IsAssignableFrom(mi.PropertyType))
                            {
                                List<string> props = new List<string>();
                                mi.PropertyType.GetProperties().ToList().ForEach(p => {
                                    if (member.PropertyType.GetProperties().Any(c => c.Name.Equals(p.Name, StringComparison.Ordinal)))
                                        props.Add(p.Name);
                                });
                                Type ex = typeof(TestDynamicSelect);
                                MethodInfo mm = ex.GetMethod("DynamicSelectGenerator2");
                                MethodInfo miConstructed = mm.MakeGenericMethod(mi.PropertyType, member.PropertyType);
                                var expr = (LambdaExpression)miConstructed.Invoke(null, new object[] { props });
                                // Probably here is not correct 
                                return Expression.Bind(member, Expression.TypeAs(expr, member.PropertyType)); 
                            }
                            else
                            {
                                var xOriginal = Expression.Property(xParameter, mi);
                                return Expression.Bind(member, xOriginal);
                            }
                        }
                    }
                    //return Expression.Constant(GetDefault(mi.PropertyType));
                    throw new Exception("No property to bind");
                }
            );

            var b = bindings.Where(x => x != null && xNew.Type.GetProperties().Any(p => p.Name.Equals(x.Member.Name, StringComparison.OrdinalIgnoreCase)));
            // initialization "new Data { Field1 = x.Field1, Field2 = x.Field2 }"
            var xInit = Expression.MemberInit(xNew, b);

            // expression "x => new Data { Field1 = x.Field1, Field2 = x.Field2 }"
            var lambda = Expression.Lambda<Func<T, TSelect>>(xInit, xParameter);

            return lambda;
        }
   }

I can't populate correctly the nested property SUPERIOR which is always null. Basically what I am trying to do is to populate DTO classes from an Entity class using property name comparison.

Please help.

0 Answers0