ExpressionVisitor не имеет своего вызова VisitMethodCall
Я следую серии примеров на MSDN по созданию поставщика LINQ и ударил стену.
Я ожидаю, что когда я напишу следующий тест, ExpressionVisitor
подкласс в исходном коде ниже имеет VisitMethodCall
вызывается.
[Fact]
public void DatabaseModeler_provides_table_modeler()
{
var q = new LightmapQuery<AspNetRoles>(new SqliteProvider2());
q.Where(role => role.Name == "Admin");
var result = q.ToList();
}
Вместо этого происходит то, что VisitConstant
метод вызывается. Я предполагаю, что это потому, что когда Provider
например, он присваивает Expression
свойство а ConstantExpression
, Я не уверен, что делаю что-то не так или в руководстве по MSDN есть проблемы, которые мешают мне получить выражение, содержащее Where
вызов метода.
Это исходный код, который у меня есть для IQueryable<T>
а также IQueryProvider
Реализации.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
namespace Lightmap.Querying
{
internal class SqliteQueryTranslator : ExpressionVisitor
{
internal StringBuilder queryBuilder;
internal string Translate(Expression expression)
{
this.queryBuilder = new StringBuilder();
this.Visit(expression);
return this.queryBuilder.ToString();
}
public override Expression Visit(Expression node)
{
return base.Visit(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.DeclaringType != typeof(IQueryable) && node.Method.Name != nameof(Enumerable.Where))
{
throw new NotSupportedException($"The {node.Method.Name} method is not supported.");
}
this.queryBuilder.Append($"SELECT * FROM {node.Method.DeclaringType.Name}");
return node;
}
protected override Expression VisitConstant(ConstantExpression node)
{
return node;
}
private static Expression StripQuotes(Expression expression)
{
while (expression.NodeType == ExpressionType.Quote)
{
expression = ((UnaryExpression)expression).Operand;
}
return expression;
}
}
public abstract class LightmapProvider : IQueryProvider
{
public IQueryable CreateQuery(Expression expression)
{
Type genericParameter = expression.GetType().GetGenericArguments().First();
return (IQueryable)Activator.CreateInstance(typeof(LightmapQuery<>)
.MakeGenericType(genericParameter), new object[] { this, expression });
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression) => new LightmapQuery<TElement>(this, expression);
object IQueryProvider.Execute(Expression expression)
{
return this.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return (TResult)this.Execute(expression);
}
public abstract string GetQueryText(Expression expression);
public abstract object Execute(Expression expression);
}
public class SqliteProvider2 : LightmapProvider
{
public override object Execute(Expression expression)
{
var x = new SqliteQueryTranslator().Translate(expression);
return Activator.CreateInstance(typeof(List<>).MakeGenericType(TypeCache.GetGenericParameter(expression.Type, t => true)));
}
public override string GetQueryText(Expression expression)
{
throw new NotImplementedException();
}
}
public class LightmapQuery<TTable> : IOrderedQueryable<TTable>
{
public LightmapQuery(IQueryProvider provider)
{
this.Provider = provider;
this.Expression = Expression.Constant(this);
}
public LightmapQuery(IQueryProvider provider, Expression expression)
{
if (!typeof(IQueryable<TTable>).IsAssignableFrom(expression.Type))
{
throw new Exception();
}
this.Expression = expression;
this.Provider = provider;
}
public Type ElementType => typeof(TTable);
public Expression Expression { get; }
public IQueryProvider Provider { get; }
public IEnumerator<TTable> GetEnumerator()
{
return (this.Provider.Execute<IEnumerable<TTable>>(Expression)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return (Provider.Execute<IEnumerable>(Expression)).GetEnumerator();
}
}
}
редактировать
Обновив мой модульный тест, чтобы фактически сохранить IQueryable
от .Where
Я все еще не вижу своего VisitMethodCall
вызывается.
[Fact]
public void DatabaseModeler_provides_table_modeler()
{
var q = new LightmapQuery<AspNetRoles>(new SqliteProvider2())
.Where(role => role.Name == "Admin");
var result = q.ToList();
}
2 ответа
После некоторых проб и ошибок и небольшой помощи в IRC я смог выявить проблему и решить ее. Проблема заключалась в том, что мой проект модульного тестирования, который был.Net Core, не имел ссылки на System.Linq.Queryable
сборка.
В моем модульном тесте
[Fact]
public void DatabaseModeler_provides_table_modeler()
{
var q = new LightmapQuery<AspNetRoles>(new SqliteProvider2());
var q2 = q.Where(role => role.Name == "Admin");
var result = q2.ToList();
}
Type
из q2
является "WhereEnumerableIterator'1"
что заставило нас понять, что это не возвращало IQueryable
,
После добавления вышеуказанной ссылки на project.json
, Type
из q2
превратился в "LightmapQuery'1"
как я и ожидал. С этим мой VisitMethodCall
Метод получает удар без проблем.
Вопрос тривиален - вы забыли назначить результат применения Where
:
q.Where(role => role.Name == "Admin");
используйте что-то подобное
var q = new LightmapQuery<AspNetRoles>(new SqliteProvider2())
.Where(role => role.Name == "Admin");
var result = q.ToList();