Я хочу создать что-то вроде удаленного исполнителя выражений 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)
Спасибо