2

LINQ プロバイダーを作成するためのMSDNのサンプル シリーズに従っていますが、壁にぶつかりました。

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メソッドが呼び出されます。これは、がインスタンス化されると、そのプロパティに a がProvider割り当てられるためだと思います。私が何か間違ったことをしているのか、それとも MSDN のガイドに問題があり、メソッド呼び出しを含む式を取得できないのかはわかりません。ExpressionConstantExpressionWhere

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();
}
4

2 に答える 2

1

いくつかの試行錯誤と 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();
}

Typeofq2は、"WhereEnumerableIterator'1"を返していないことに気づきましたIQueryable

ここに画像の説明を入力

上記の参照を に追加すると、project.jsonは期待どおりにTypeなりq2ました。"LightmapQuery'1"これで、私のVisitMethodCallメソッドは問題なくヒットします。

ここに画像の説明を入力

于 2016-02-15T19:37:58.077 に答える
0

問題は些細なことです-適用の結果を割り当てるのを忘れましたWhere:

q.Where(role => role.Name == "Admin");

代わりにこのようなものを使用してください

var q = new LightmapQuery<AspNetRoles>(new SqliteProvider2())
    .Where(role => role.Name == "Admin");
var result = q.ToList();
于 2016-02-15T17:48:04.777 に答える