Skip to content

Hey, I love the library but it's missing the ability to select nested properties (I modified the code to allow it but not sure how to become a contributor) #3

@CShelton11

Description

@CShelton11
	// Following code will allow you to use your library with nested properties
	// Hopefully I have everything included, let me know if I missed anything.  
	// Example - Would work with property "Claim.ClaimReason.ClaimReasonDescription"

	
	`public static IQueryable<dynamic> SelectProperties<T>(this IQueryable<T> source, IEnumerable<String> propertyNames)
    {
        if (source == null) throw new ArgumentNullException("Source Object is NULL");

        var sourceItem = Expression.Parameter(source.ElementType, "t");
        var sourceProperties = propertyNames.Where(name => source.ElementType.HasProperty(name) == true).ToDictionary(name => name, name => source.ElementType.GetPropertyInfo(name));
        var dynamicType = DynamicTypeBuilder.GetDynamicType(sourceProperties.Values.ToDictionary(f => f.Name, f => f.PropertyType), typeof(object), Type.EmptyTypes);
        var bindings = dynamicType.GetFields().Select(p => Expression.Bind(p, BuildMemberExpression(sourceProperties.FirstOrDefault(a => a.Value.Name == p.Name).Key, sourceItem))).OfType<MemberBinding>().ToList();
        var selector = Expression.Lambda<Func<T, dynamic>>(Expression.MemberInit(Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem);

        return source.Select(selector);
    }

	private static MemberExpression GetInitalMemberExpression(string v, ParameterExpression parameterExpression)
    {
        return Expression.Property(parameterExpression, v);
    }

    private static MemberExpression BuildMemberExpression(String selector, ParameterExpression expression, Int32 index = 0)
    {
        var segments = selector.Split((".").ToCharArray()).ToList();
        MemberExpression memberExpression = GetInitalMemberExpression(segments[0], expression);
        return BuildMemberExpression(segments, memberExpression, ++index);
    }

    private static MemberExpression BuildMemberExpression(List<String> segments, MemberExpression finalExpression, Int32 index = 0)
    {
        if (index >= segments.Count)
        {
            return finalExpression;
        }
        finalExpression = Expression.Property(finalExpression, segments[index]);
        return BuildMemberExpression(segments, finalExpression, ++index);
    }
	
	public static Boolean HasProperty(this Type type, String name)
    {
        var parts = name.Split('.');
        if (parts.Length == 1)
        {
            return type.GetProperty(name) == null ? false : true;
        }
        else
        {
            var part1 = parts[0];
            var part2 = String.Join(".", parts.Where(a => a != part1));
            var prop1 = type.GetProperty(part1);
            if (prop1 == null) { return false; }
            else { return prop1.PropertyType.HasProperty(part2); }
        }
    }

    public static PropertyInfo GetPropertyInfo(this Type type, String name)
    {
        var parts = name.Split('.');
        if (parts.Length == 1)
        {
            return type.GetProperty(name);
        }
        else
        {
            var part1 = parts[0];
            var part2 = String.Join(".", parts.Where(a => a != part1));
            var prop1 = type.GetProperty(part1);
            if (prop1 == null) { return null; }
            else { return prop1.PropertyType.GetPropertyInfo(part2); }
        }
    }
	`

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions