Что IQueryable.Provider.CreateQuery <T>предоставит? - PullRequest
0 голосов
/ 03 января 2019

Я хочу создать что-то вроде удаленного исполнителя выражений Linq с EF core 2.

Сначала пользователь отправляет запрос на сервер

2) сервер сгенерирует выражение, которое может бытьвыполнить по требованию пользователя

что-то вроде: {value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1[X.API.NewsPaper]).Where(model => (model.Id.ToString() == "630b7340-068e-47be-ba0e-2e03bdd72e1e")).AsNoTracking().FirstOrDefault().Posts}

Затем сервер сериализует его на Aq.ExpressionJsonSerializer и поместит его в свой ответ (примечание: сгенерированное выражение может отличаться в зависимости от клиентазапрос и схема базы данных, поэтому я ничего не знаю о базе данных, поэтому я не могу использовать предопределенное выражение)

3) Клиент запросит выполнение сгенерированного выражения, отправив его на сервер

4) Сервер попытается десериализовать его с помощью моей пользовательской функции Aq.ExpressionJsonSerializer library

, и здесь появится проблема:

после десериализации выражения я не могу выполнить какую-либо функцию для него.

Вот моя пользовательская Aq.ExpressionJsonSerializer функция

namespace Aq.ExpressionJsonSerializer {
    partial class Deserializer {
        private ConstantExpression ConstantExpression (
            ExpressionType nodeType, System.Type type, JObject obj) {
            object value;

            var valueTok = this.Prop (obj, "value");
            if (valueTok == null || valueTok.Type == JTokenType.Null) {
                value = null;
            } else {
                var valueObj = (JObject) valueTok;
                var valueType = this.Prop (valueObj, "type", this.Type);

                if (typeof(IQueryable).IsAssignableFrom(valueType)) {
                    value = this.queryableProvider.GetIQueryableByType(valueType.GetGenericArguments().FirstOrDefault());
                } else {
                    value = this.Deserialize (this.Prop (valueObj, "value"), valueType);
                }
            }

            switch (nodeType) {
                case ExpressionType.Constant:
                    return Expr.Constant (value, type);
                default:
                    throw new NotSupportedException ();
            }
        }
    }
}

Вот реализация интерфейса:

public IQueryable GetIQueryableByType (Type type) {
    ExpressionQueryableType = type;
    return this.GetType ()
        .GetMethod (nameof (GetIQueryable))
        .MakeGenericMethod (type)
        .Invoke (this, null) as IQueryable;
}

public IQueryable<T> GetIQueryable<T> () where T : class =>
    dbContext.Set<T> ().Select (x => x);

Вот мой код контроллера:

public async Task<IActionResult> PagedListExp (Cursor cursor) {
    var defaultSettings = new JsonSerializerSettings {
        MaxDepth = 15,
        TypeNameHandling = TypeNameHandling.Objects
    };
    defaultSettings.Converters.Add (new ExpressionJsonConverter (Assembly.GetAssembly (typeof (MyController)), this));
    var path = JsonConvert.DeserializeObject<Expression> (cursor.ExpressionPath, defaultSettings);

    var queryable =
        this.GetType ()
        .GetMethod (nameof (GetIQueryableFromExpression))
        .MakeGenericMethod (ExpressionQueryableType, modelParser.GetResourceType(cursor.IssuedFor))
        .Invoke (this, new object[] { path }) 
       // as IQueryable<dynamic>;
        as IQueryable<Post>

    var size = queryable.Count();

    ...

}

public IQueryable<TTarget> GetIQueryableFromExpression<TSource, TTarget> (Expression exp) where TSource : class {
    return dbContext.Set<TSource> ().AsQueryable<TSource> ().Provider.CreateQuery<TTarget> (exp);
}

А вот и сообщение об ошибке:

An unhandled exception occurred while processing the request.
ArgumentException: Expression of type 'System.Collections.Generic.ICollection`1[X.API.Post]' cannot be used for parameter of type 'System.Linq.IQueryable`1[X.API.Post]' of method 'Int32 Count[Post](System.Linq.IQueryable`1[X.API.Post])'
Parameter name: arg0

System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, string methodParamName, string argumentParamName, int index)

Спасибо

...