如何重载 LINQ string.Contains()?
How do I overload LINQ string.Contains()?
对于我正在实施的通用过滤器,我需要修改以下内容以接受将在 Where 方法中搜索的列名称,例如:
public IQueryable<TEntity> GetEntities(string val)
{
TEntity entity = _DbContext.Set<TEntity>()
.Where(e => e.Col1.Contains(val));
return entities;
}
改为
public IQueryable<TEntity> GetEntities(string val, string colName)
{
TEntity entity = _DbContext.Set<TEntity>()
.WhereContains(val, colName);
return entities;
}
colName 是字符串列的名称。
我查看了 https://blog.jeremylikness.com/blog/dynamically-build-linq-expressions/,但无法根据我的需要修改那里的示例。答案应该是
的形式
public static IQueryable<TEntity> WhereContains<TEntity>(this IQueryable<TEntity> query, string value, string colName)
where TEntity : class
{
...
...
}
但我无法让它工作...
好的,找到了一个好的reference并且能够修改它:
public static IQueryable<T> TextFilter<T>(IQueryable<T> source, string[] colNames, string[] terms)
{
if (colNames.Length == 0) return source;
// T is a compile-time placeholder for the element type of the query.
Type elementType = typeof(T);
// Get all the properties on this specific type for colNames.
List<PropertyInfo> props = new List<PropertyInfo>();
for (int i = 0; i < colNames.Length; i++)
{
PropertyInfo prop = elementType.GetProperties().Where(x => x.PropertyType == typeof(string) && x.Name.ToLower() == colNames[i].ToLower()).FirstOrDefault();
if (prop == null) { return source; }
props.Add(prop);
}
// Get the right overload of String.Contains. Can be replaced e.g. with "Contains"
MethodInfo containsMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) })!;
// Create a parameter for the expression tree:
// the 'x' in 'x => x.PropertyName.Contains("term")'
// The type of this parameter is the query's element type
ParameterExpression prm = Expression.Parameter(elementType);
// Map each property to an expression tree node
List<Expression> expressions = new List<Expression>();
for (int i = 0; i < colNames.Length; i++)
{
expressions.Add(
Expression.Call(
Expression.Property(
prm,
props.ElementAt(i)
),
containsMethod,
Expression.Constant(terms[i])
)
);
}
// Combine all the resultant expression nodes using ||
Expression body = expressions
.Aggregate(
(prev, current) => Expression.And(prev, current)
);
// Wrap the expression body in a compile-time-typed lambda expression
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(body, prm);
// Because the lambda is compile-time-typed (albeit with a generic parameter), we can use it with the Where method
return source.Where(lambda);
}
我无法将它作为扩展来工作,所以调用它是通过以下方式完成的:
var qry= QueryableExtensions.TextFilter(_crudApiDbContext.Set<TEntity>()
.Where(entity => entity.someColumn==someValue),
filters.Keys.ToArray(), filters.Values.ToArray());
List<TEntity> entities = await qry
.Skip(pageSize * (page - 1)).Take(pageSize).ToListAsync();
对于我正在实施的通用过滤器,我需要修改以下内容以接受将在 Where 方法中搜索的列名称,例如:
public IQueryable<TEntity> GetEntities(string val)
{
TEntity entity = _DbContext.Set<TEntity>()
.Where(e => e.Col1.Contains(val));
return entities;
}
改为
public IQueryable<TEntity> GetEntities(string val, string colName)
{
TEntity entity = _DbContext.Set<TEntity>()
.WhereContains(val, colName);
return entities;
}
colName 是字符串列的名称。
我查看了 https://blog.jeremylikness.com/blog/dynamically-build-linq-expressions/,但无法根据我的需要修改那里的示例。答案应该是
的形式public static IQueryable<TEntity> WhereContains<TEntity>(this IQueryable<TEntity> query, string value, string colName)
where TEntity : class
{
...
...
}
但我无法让它工作...
好的,找到了一个好的reference并且能够修改它:
public static IQueryable<T> TextFilter<T>(IQueryable<T> source, string[] colNames, string[] terms)
{
if (colNames.Length == 0) return source;
// T is a compile-time placeholder for the element type of the query.
Type elementType = typeof(T);
// Get all the properties on this specific type for colNames.
List<PropertyInfo> props = new List<PropertyInfo>();
for (int i = 0; i < colNames.Length; i++)
{
PropertyInfo prop = elementType.GetProperties().Where(x => x.PropertyType == typeof(string) && x.Name.ToLower() == colNames[i].ToLower()).FirstOrDefault();
if (prop == null) { return source; }
props.Add(prop);
}
// Get the right overload of String.Contains. Can be replaced e.g. with "Contains"
MethodInfo containsMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) })!;
// Create a parameter for the expression tree:
// the 'x' in 'x => x.PropertyName.Contains("term")'
// The type of this parameter is the query's element type
ParameterExpression prm = Expression.Parameter(elementType);
// Map each property to an expression tree node
List<Expression> expressions = new List<Expression>();
for (int i = 0; i < colNames.Length; i++)
{
expressions.Add(
Expression.Call(
Expression.Property(
prm,
props.ElementAt(i)
),
containsMethod,
Expression.Constant(terms[i])
)
);
}
// Combine all the resultant expression nodes using ||
Expression body = expressions
.Aggregate(
(prev, current) => Expression.And(prev, current)
);
// Wrap the expression body in a compile-time-typed lambda expression
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(body, prm);
// Because the lambda is compile-time-typed (albeit with a generic parameter), we can use it with the Where method
return source.Where(lambda);
}
我无法将它作为扩展来工作,所以调用它是通过以下方式完成的:
var qry= QueryableExtensions.TextFilter(_crudApiDbContext.Set<TEntity>()
.Where(entity => entity.someColumn==someValue),
filters.Keys.ToArray(), filters.Values.ToArray());
List<TEntity> entities = await qry
.Skip(pageSize * (page - 1)).Take(pageSize).ToListAsync();