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

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); }
        }
    }
	`

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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