下面是我的问题的简单演示代码.
[TestClass]
public class ExpressionTests
{
[TestMethod]
public void TestParam()
{
Search<Student>(s => s.Id == 1L);
GetStudent(1L);
}
private void GetStudent(long id)
{
Search<Student>(s => s.Id == id);
}
private void Search<T>(Expression<Func<T, bool>> filter)
{
var visitor = new MyExpressionVisitor();
visitor.Visit(filter);
}
}
public class MyExpressionVisitor : ExpressionVisitor
{
protected override Expression VisitConstant(ConstantExpression node)
{
Assert.AreEqual(1L, node.Value);
return base.VisitConstant(node);
}
}
Run Code Online (Sandbox Code Playgroud)
TestParam方法导致VisitConstant在两个不同的路径上调用:
1. TestParam - > Search- >VisitConstant
在此执行路径中,传递给Search方法的常量表达式(1L)是实常数值.在这里,一切都很好,断言按预期成功.当VisitConstant通过第一路径调用node.Value.GetType()是 …
使用下面的示例我想Expression在我的Contains方法中使用我的内容,让它使用EF.将查询传递到sql server .
我怎样才能使这个正常工作?
void Main()
{
IQueryable<Person> qry = GetQueryableItemsFromDB();
var filtered = qry.Filter(p=>p.CompanyId);
}
public static class Ext
{
public static IQueryable<T> Filter<T>(this IQueryable<T> items, Expression<Func<T, int>> resolveCompanyIdExpression)
{
IEnumerable<int> validComps = GetCompanyIdsFromDataBase();
var exp = Expression.Lambda<Func<T, bool>>(
Expression.Call(typeof(Queryable),"Contains", new[] { typeof(Company) },
Expression.Constant(validComps),
resolveCompanyIdExpression.Body),
resolveCompanyIdExpression.Parameters[0]);
return items.Where(exp);
}
public static IQueryable<T> Filter<T>(this IQueryable<T> items, Expression<Func<T, IEnumerable<int>>> resolveCompanyIdExpression)
{
IEnumerable<int> validComps = GetCompanyIdsFromDataBase();
//No Idea what to do here ?
}
}
public class …Run Code Online (Sandbox Code Playgroud) 鉴于此代码:
static void Main(string[] args)
{
Expression<Func<SomeDerivedClass, object>> test = i => i.Prop;
var body = (UnaryExpression) test.Body;
Console.WriteLine(((MemberExpression) body.Operand).Member.ReflectedType);
}
public class SomeClass
{
public int Prop { get; private set; }
}
public class SomeDerivedClass : SomeClass
{
}
Run Code Online (Sandbox Code Playgroud)
我希望ReflectedType是SomeDerivedClass,因为它是表达式的参数类型.但它是SomeClass,如果我理解正确的话 - 声明类型.
这是为什么?
我已经阅读了这个答案并从中理解了它突出显示的具体情况,即当你在另一个lambda中有一个lambda并且你不想意外地让内部lambda也用外部lambda编译时.编译外部时,您希望内部lambda表达式保留为表达式树.在那里,是的,引用内部lambda表达式是有道理的.
但我相信这就是它.是否还有其他用例来引用lambda表达式?
如果没有,为什么所有LINQ运算符,即IQueryable<T>在Queryable类中声明的扩展引用谓词或lambda作为参数,当它们将信息打包在MethodCallExpression.
我尝试了一个例子(以及过去几天的其他一些例子),在这种情况下引用lambda似乎没有任何意义.
这是一个方法调用表达式,该方法需要一个lambda表达式(而不是委托实例)作为唯一参数.
然后我MethodCallExpression通过将其包装在lambda中来编译它.
但是,这也不会编译内部LambdaExpression(GimmeExpression方法的参数).它将内部lambda表达式保留为表达式树,并且不创建它的委托实例.
事实上,它没有引用它很好.
如果我引用该参数,它会中断并给出一个错误,表明我向该GimmeExpression方法传递了错误的参数类型.
这是怎么回事?这引用的内容是什么?
private static void TestMethodCallCompilation()
{
var methodInfo = typeof(Program).GetMethod("GimmeExpression",
BindingFlags.NonPublic | BindingFlags.Static);
var lambdaExpression = Expression.Lambda<Func<bool>>(Expression.Constant(true));
var methodCallExpression = Expression.Call(null, methodInfo, lambdaExpression);
var wrapperLambda = Expression.Lambda(methodCallExpression);
wrapperLambda.Compile().DynamicInvoke();
}
private static void GimmeExpression(Expression<Func<bool>> exp)
{
Console.WriteLine(exp.GetType());
Console.WriteLine("Compiling and executing expression...");
Console.WriteLine(exp.Compile().Invoke());
}
Run Code Online (Sandbox Code Playgroud) 我已经实现了一个基本的(天真的?)LINQ提供程序,它可以用于我的目的,但是我想解决一些怪癖,但我不确定如何.例如:
// performing projection with Linq-to-Objects, since Linq-to-Sage won't handle this:
var vendorCodes = context.Vendors.ToList().Select(e => e.Key);
Run Code Online (Sandbox Code Playgroud)
我的IQueryProvider实现有一个CreateQuery<TResult>看起来像这样的实现:
public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
{
return (IQueryable<TResult>)Activator
.CreateInstance(typeof(ViewSet<>)
.MakeGenericType(elementType), _view, this, expression, _context);
}
Run Code Online (Sandbox Code Playgroud)
显然,当Expressiona是a MethodCallExpression并且TResult是a时string,这会产生窒息,所以我想我会执行这个糟糕的事情:
public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
{
var elementType = TypeSystem.GetElementType(expression.Type);
if (elementType == typeof(EntityBase))
{
Debug.Assert(elementType == typeof(TResult));
return (IQueryable<TResult>)Activator.CreateInstance(typeof(ViewSet<>).MakeGenericType(elementType), _view, this, expression, _context);
}
var methodCallExpression = expression as MethodCallExpression;
if(methodCallExpression != null …Run Code Online (Sandbox Code Playgroud) 查看该Expression.Call(),方法可用的文档重载,我可以找到以下重载来获取一个表达式节点,该节点将执行对期望的实例方法的调用:
Expression数组的变量参数IEnumerable<Expression>没有超载期望单个参数的理由是什么?
在我看来,单个参数案例的方法签名是:
public static MethodCallExpression Call(
Expression instance,
MethodInfo method,
Expression arg0);
Run Code Online (Sandbox Code Playgroud)
我没有看到任何其他重载会与此方法签名冲突,所以我真的不明白为什么该方法丢失.我明白,期望一个数组或一个数组的重载IEnumerable将允许我创建一个Expression单参数的情况,但这也适用于其他可用的重载,所以我很好奇,如果有什么我看不到会解释为什么这种重载缺失了.
首先,这与为什么从表达式>> Func <>创建的Func <>不一样直接声明?令人惊讶的恰恰相反.另外,我在研究这个问题时发现的所有链接和问题都源自2010 - 2012年的时间段,所以我决定在这里打开一个新问题,看看是否有关于当前代表状态的讨论.NET生态系统中的行为.
也就是说,我正在使用.NET Core 2.0和.NET 4.7.1,并且我看到了一些奇怪的性能指标,这些指标是从编译表达式创建的委托与被描述和声明为CLR对象的委托相关的.
关于我如何偶然发现这个问题的一些背景,我正在做一个测试,涉及1,000和10,000个对象的数组中的数据选择,并注意到如果我使用编译表达式,它会获得更快的结果.我设法把它归结为一个非常简单的项目,可以重现这个问题,你可以在这里找到:
https://github.com/Mike-EEE/StackOverflow.Performance.Delegates
对于测试,我使用了两组基准测试,其特点是与已声明的委托配对的已编译委托,从而产生四个总核心基准.
第一个委托集由一个返回空字符串的空委托组成.第二个集合是一个委托,其中包含一个简单的表达式.我想证明这个问题出现在最简单的委托以及具有已定义主体的委托中.
然后,这些测试通过优秀的Benchmark.NET 性能产品在CLR运行时和.NET Core运行时上运行,从而产生8个总基准.此外,我还利用了同样出色的Benchmark.NET 反汇编诊断程序来发出基准测量的JIT期间遇到的反汇编.我将分享以下结果.
以下是运行基准测试的代码.你可以看到它非常简单:
[CoreJob, ClrJob, DisassemblyDiagnoser(true, printSource: true)]
public class Delegates
{
readonly DelegatePair<string, string> _empty;
readonly DelegatePair<string, int> _expression;
readonly string _message;
public Delegates() : this(new DelegatePair<string, string>(_ => default, _ => default),
new DelegatePair<string, int>(x => x.Length, x => x.Length)) {}
public Delegates(DelegatePair<string, string> empty, DelegatePair<string, int> expression,
string message = "Hello World!")
{
_empty = …Run Code Online (Sandbox Code Playgroud) 我试图用来GetCustomAttributes()获取属性上定义的属性.问题是该属性是一个被覆盖的属性,我无法弄清楚如何从表达式中提取被覆盖的属性.我只能弄清楚如何获得基类.
这是一些代码
public class MyAttribute : Attribute
{
//...
}
public abstract class Text
{
public abstract string Content {get; set;}
}
public class Abstract : Text
{
[MyAttribute("Some Info")]
public override string Content {get; set;}
}
Run Code Online (Sandbox Code Playgroud)
现在我试图MyAttribute摆脱抽象类.但我需要通过一个Expression.这就是我一直在使用的:
Expression<Func<Abstract, string>> expression = c => c.Content;
Expression exp = expression.Body;
MemberInfo memberType = (exp as MemberExpression).Member;
var attrs = Attribute.GetCustomAttributes(memberType, true);
Run Code Online (Sandbox Code Playgroud)
不幸的是atts最终为空.问题是menberType最终是为了Text.Content而不是Abstract.Content班级.因此,当我获得属性时,它什么都不返回.
我正在使用下面的方法来查询EF
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
Run Code Online (Sandbox Code Playgroud)
现在我想创建动态Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>表达式来订购我的数据.
我只知道字段名称为字符串和顺序类型(升序,降序)为字符串(asc,desc)
我想指定一个可以接受方法的参数,而不必指定泛型参数来生成给定方法的MethodInfo.
例如,我想写这样的代码:
interface IService
{
object AMethod(int p1, int p2);
}
IThingy<IService>() thingy;
thingy.Add(svc => svc.AMethod);
Run Code Online (Sandbox Code Playgroud)
我能提供的最接近的选择是:
interface IThingy<TService>
{
void Add1<T0, T1, TResult>(Expression<Func<TService, Func<T0, T1, TResult>>> expression);
void Add2(Expression<Func<TService, Func<int, int, object>>> expression);
void Add3(Expression<Action<TService>> expression);
}
thingy.Add1<int, int, object>(svc => svc.AMethod);
thingy.Add2(svc => svc.AMethod);
thingy.Add3(svc => svc.AMethod(0, 0));
Run Code Online (Sandbox Code Playgroud)
Add1意味着很多Func重载,我很好,但是如果不指定泛型参数就无法调用.
Add2不需要通用参数,但暗示每个参数签名都有特定的重载.
Add3需要调用该方法,包括伪参数.
仅供参考,我将处理表达式以获取给定方法的MethodInfo,如下所示:
MemberInfo GetMemberFromExpression<T>(Expression<ActionT>> expression)
{
return ((MethodCallExpression)expression.Body).Method
}
GetMemberFromExpression(svc => svc.AMethod(0, 0));
Run Code Online (Sandbox Code Playgroud) linq-expressions ×10
c# ×9
.net ×4
linq ×3
iqueryable ×2
lambda ×2
c#-5.0 ×1
closures ×1
delegates ×1
dynamic ×1
performance ×1