使用 Any 针对任何类型构建 EF 兼容表达式调用
Building a EF Compatible expression call using Any against any type
相关文章:
LINQ Expression Tree: Call Any method against a DbSet
我希望能够动态构建一个 where 语句来再次检查 属性 的值列表。
静态调用可能如下所示:
IEnumerable<Guid> ids = //.... list of guids
context.DbSet<Person>()
.Where(p => ids.Any(i => i == p.id))
.ToList();
Table
[Table("Person")]
class Person
{
public Guid Id { get; set; }
public string GivenName { get; set; }
public string FamilyName { get; set; }
public int ChildrenCount { get; set; }
}
我通过 JSON 接收数据,所以它不是强类型的:
class Query
{
public string PropertyName { get; set; } // 'Id' or 'FamilyName' or 'ChildrenCount'
public string AnyValues { get; set; } // 'guid1,guid2' or 'Musk,Gates' or '1,2,3' respectively
}
与核心问题相关的主要工作示例:
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var query = new Query
{
PropertyName = "id",
AnyValues = new List<Guid> { Guid.Empty }
};
var exp = ToExpression<Person>(query);
var people = context.DbSet<Person>().Where(exp).ToListAsync();
}
public static Expression ToExpression<TModel>(Query query)
{
Expression result = null;
var propInfo = typeof(TModel).GetProperty(query.PropertyName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
if (propInfo != null)
{
var propType = propInfo.PropertyType;
var parameterExpression = Expression.Parameter(typeof(TModel), nameof(TModel));
var memberExpression = Expression.Property(parameterExpression, query.PropertyName);
var constantExpression = GetConstantExpressionForType(propType, query.AnyValues);
if (memberExpression != null && constantExpression != null)
{
//
// var org = Expression.Parameter(typeof(Organization), "org");
// Expression<Func<OrganizationField, bool>> predicate = a => a.CustomField.Name == filter.Name && values.Contains(a.Value);
// var body = Expression.Call(typeof(Enumerable), "Any", new[] { typeof(OrganizationField) },
// Expression.PropertyOrField(org, "OrganizationFields"), predicate);
// var lambda = Expression.Lambda<Func<Organization, bool>>(body, org);
// STUCK HERE
result = Expression.Call(
typeof(Enumerable),
"Any",
?? );
}
}
return result;
}
// Just a Hack for the example, not relevant to the question itself
public static ConstantExpression GetConstantExpressionForType(Type type, object values)
{
ConstantExpression result = null;
if (type == typeof(Guid))
{
result = Expression.Constant(values, typeof(IEnumerable<Guid>));
}
else if (type == typeof(string))
{
result = Expression.Constant(values, typeof(IEnumerable<string>));
}
else if (type == typeof(int))
{
result = Expression.Constant(values, typeof(IEnumerable<int>));
}
return result;
}
}
public class Person
{
public Guid Id { get; set; }
public string GivenName { get; set; }
public string FamilyName { get; set; }
public int ChildrenCount { get; set; }
}
public class Query
{
public string PropertyName { get; set; } // 'Id' or 'FamilyName' or 'ChildrenCount'
//public string AnyValues { get; set; } // 'guid1,guid2' or 'Musk,Gates' or '1,2,3' respectively
public object AnyValues { get; set; } // to simply the core of the question
}
使用以下扩展方法:
public static class QueryableExtensions
{
public static IQueryable<T> AnyFromItems<T>(this IQueryable<T> source, string items, string propName)
{
var strItems = items.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var entityParam = Expression.Parameter(typeof(T), "e");
var propExpression = Expression.PropertyOrField(entityParam, propName);
var itemType = propExpression.Type;
var itemParam = Expression.Parameter(itemType, "i");
var anyPredicate =
Expression.Lambda(
Expression.Equal(itemParam, propExpression),
itemParam);
// apply conversion
var itemsExpression = Expression.Call(typeof(QueryableExtensions), nameof(QueryableExtensions.ParseItems),
new[] { itemType }, Expression.Constant(strItems));
var filterLambda =
Expression.Lambda<Func<T, bool>>(
Expression.Call(typeof(Enumerable), nameof(Enumerable.Any), new[] { itemType },
itemsExpression, anyPredicate),
entityParam);
return source.Where(filterLambda);
}
private static IEnumerable<TItem> ParseItems<TItem>(IEnumerable<string> items)
{
return items.Select(i => (TItem)Convert.ChangeType(i, typeof(TItem)));
}
}
和用法:
var query = new Query
{
PropertyName = "id",
AnyValues = string.Join(", ", new List<Guid> { Guid.Empty });
};
var people = await context.DbSet<Person>()
.AnyFromItems(query.AnyValues, query.PropertyName)
.ToListAsync();
相关文章:
LINQ Expression Tree: Call Any method against a DbSet
我希望能够动态构建一个 where 语句来再次检查 属性 的值列表。
静态调用可能如下所示:
IEnumerable<Guid> ids = //.... list of guids
context.DbSet<Person>()
.Where(p => ids.Any(i => i == p.id))
.ToList();
Table
[Table("Person")]
class Person
{
public Guid Id { get; set; }
public string GivenName { get; set; }
public string FamilyName { get; set; }
public int ChildrenCount { get; set; }
}
我通过 JSON 接收数据,所以它不是强类型的:
class Query
{
public string PropertyName { get; set; } // 'Id' or 'FamilyName' or 'ChildrenCount'
public string AnyValues { get; set; } // 'guid1,guid2' or 'Musk,Gates' or '1,2,3' respectively
}
与核心问题相关的主要工作示例:
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var query = new Query
{
PropertyName = "id",
AnyValues = new List<Guid> { Guid.Empty }
};
var exp = ToExpression<Person>(query);
var people = context.DbSet<Person>().Where(exp).ToListAsync();
}
public static Expression ToExpression<TModel>(Query query)
{
Expression result = null;
var propInfo = typeof(TModel).GetProperty(query.PropertyName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
if (propInfo != null)
{
var propType = propInfo.PropertyType;
var parameterExpression = Expression.Parameter(typeof(TModel), nameof(TModel));
var memberExpression = Expression.Property(parameterExpression, query.PropertyName);
var constantExpression = GetConstantExpressionForType(propType, query.AnyValues);
if (memberExpression != null && constantExpression != null)
{
//
// var org = Expression.Parameter(typeof(Organization), "org");
// Expression<Func<OrganizationField, bool>> predicate = a => a.CustomField.Name == filter.Name && values.Contains(a.Value);
// var body = Expression.Call(typeof(Enumerable), "Any", new[] { typeof(OrganizationField) },
// Expression.PropertyOrField(org, "OrganizationFields"), predicate);
// var lambda = Expression.Lambda<Func<Organization, bool>>(body, org);
// STUCK HERE
result = Expression.Call(
typeof(Enumerable),
"Any",
?? );
}
}
return result;
}
// Just a Hack for the example, not relevant to the question itself
public static ConstantExpression GetConstantExpressionForType(Type type, object values)
{
ConstantExpression result = null;
if (type == typeof(Guid))
{
result = Expression.Constant(values, typeof(IEnumerable<Guid>));
}
else if (type == typeof(string))
{
result = Expression.Constant(values, typeof(IEnumerable<string>));
}
else if (type == typeof(int))
{
result = Expression.Constant(values, typeof(IEnumerable<int>));
}
return result;
}
}
public class Person
{
public Guid Id { get; set; }
public string GivenName { get; set; }
public string FamilyName { get; set; }
public int ChildrenCount { get; set; }
}
public class Query
{
public string PropertyName { get; set; } // 'Id' or 'FamilyName' or 'ChildrenCount'
//public string AnyValues { get; set; } // 'guid1,guid2' or 'Musk,Gates' or '1,2,3' respectively
public object AnyValues { get; set; } // to simply the core of the question
}
使用以下扩展方法:
public static class QueryableExtensions
{
public static IQueryable<T> AnyFromItems<T>(this IQueryable<T> source, string items, string propName)
{
var strItems = items.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var entityParam = Expression.Parameter(typeof(T), "e");
var propExpression = Expression.PropertyOrField(entityParam, propName);
var itemType = propExpression.Type;
var itemParam = Expression.Parameter(itemType, "i");
var anyPredicate =
Expression.Lambda(
Expression.Equal(itemParam, propExpression),
itemParam);
// apply conversion
var itemsExpression = Expression.Call(typeof(QueryableExtensions), nameof(QueryableExtensions.ParseItems),
new[] { itemType }, Expression.Constant(strItems));
var filterLambda =
Expression.Lambda<Func<T, bool>>(
Expression.Call(typeof(Enumerable), nameof(Enumerable.Any), new[] { itemType },
itemsExpression, anyPredicate),
entityParam);
return source.Where(filterLambda);
}
private static IEnumerable<TItem> ParseItems<TItem>(IEnumerable<string> items)
{
return items.Select(i => (TItem)Convert.ChangeType(i, typeof(TItem)));
}
}
和用法:
var query = new Query
{
PropertyName = "id",
AnyValues = string.Join(", ", new List<Guid> { Guid.Empty });
};
var people = await context.DbSet<Person>()
.AnyFromItems(query.AnyValues, query.PropertyName)
.ToListAsync();