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.