继上一个问题(Dynamically generate lambda expression with constants from variables)后,我的目标是将解决方法应用于现有代码中的大量复杂LINQ EF查询。
例如,查询构建如下:
var query = Entities.Users.Where(u => u.UserName == varUserName);
query = query.Where(u => u.IsUserLocked == varUserLocked);
query = query.Where(u => u.LastModifiedAt > varLastModifiedAt);
然后结果推导为:
var results = query.ToList();
要解决我正在使用的EF提供程序中的问题,我必须修改Where()表达式,如下所示:
var query = Entities.Users.Where(OracleEFQueryUtils.ReplaceVariablesWithConstants<Func<Entity.User, bool>>(u => u.UserName == varUserName));
query = query.Where(OracleEFQueryUtils.ReplaceVariablesWithConstants<Func<Entity.User, bool>>(u => u.IsUserLocked == varUserLocked));
query = query.Where(OracleEFQueryUtils.ReplaceVariablesWithConstants<Func<Entity.User, bool>>(u => u.LastModifiedAt > varLastModifiedAt));
可以看出,虽然这有效,但它会产生非常冗长的代码,这也意味着必须迁移大型现有代码库才能使用这种新方法。
我正在寻找一种简单的方法将此方法应用于所有Where()表达式。
到目前为止,我已经想到了我正在研究的两种方法:
我更喜欢方法2,因为它直观地感觉更干净,但是我遇到了无法找到要修改的组合表达式树的问题。我找到了IQueryable.Expression属性,但我找不到如何从那里继续。
作为参考,我对OracleEFQueryUtils的实现如下:
class OracleEFQueryUtils
{
public static Expression<TDelegate> ReplaceVariablesWithConstants<TDelegate>(Expression<TDelegate> source)
{
return source.Update(
new ReplaceVariablesWithConstantsVisitor().Visit(source.Body),
source.Parameters);
}
class ReplaceVariablesWithConstantsVisitor : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
var expression = Visit(node.Expression);
if (expression is ConstantExpression)
{
var variable = ((ConstantExpression)expression).Value;
var value = node.Member is FieldInfo ?
((FieldInfo)node.Member).GetValue(variable) :
((PropertyInfo)node.Member).GetValue(variable);
return Expression.Constant(value, node.Type);
}
return node.Update(expression);
}
}
}
我可能正在尝试做一些框架不支持的事情,但我期待任何想法!感谢。
答案 0 :(得分:1)
我要研究创建扩展以覆盖您想要支持的查询操作的方法。根据你想要的程度,它可以产生一些漂亮的代码。
public interface IOracleQueryable<T> : IQueryable<T> { }
public static class OracleQueryExtensions
{
public static IOracleQueryable<TSource> AsOracleQueryable<TSource>(
this IQueryable<TSource> source) => new OracleQueryable<TSource>(source);
public static IOracleQueryable<TSource> Where<TSource>(
this IOracleQueryable<TSource> source, Expression<Func<TSource, bool>> predicate) =>
Queryable.Where(source, Replace(predicate)).AsOracleQueryable();
private static Expression<TDelegate> Replace<TDelegate>(Expression<TDelegate> expr) =>
OracleEFQueryUtils.ReplaceVariablesWithConstants<TDelegate>(expr);
private class OracleQueryable<TSource> : IOracleQueryable<TSource>
{
public OracleQueryable(IQueryable<TSource> source) { Source = source; }
private IQueryable<TSource> Source { get; }
public Expression Expression => Source.Expression;
public IQueryProvider Provider => Source.Provider;
public Type ElementType => Source.ElementType;
public IEnumerator<TSource> GetEnumerator() => Source.GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() =>
GetEnumerator();
}
}
然后,您现在需要做的就是记住在查询中添加AsOracleQueryable()
。
var query = Entities.Users.AsOracleQueryable()
.Where(u => u.UserName == varUserName)
.Where(u => u.IsUserLocked == varUserLocked)
.Where(u => u.LastModifiedAt > varLastModifiedAt);
它甚至可以在查询语法中正常工作。
var query =
from u in Entities.Users.AsOracleQueryable()
where u.UserName == varUserName
where u.IsUserLocked == varUserLocked
where u.LastModifiedAt > varLastModifiedAt
select u;