Я создаю универсальный интерфейс для предоставления выбранных свойств строки из класса, а затем я хочу найти текст внутри каждого из этих полей, чтобы проверить, соответствует ли он.
ВотМой IFieldExposer
интерфейс:
using System;
using System.Collections.Generic;
public interface IFieldExposer<T>
{
IEnumerable<Func<T, string>> GetFields();
}
Теперь я реализую это в моем DataClass
, чтобы раскрыть свойства, которые я хотел бы повторить.Обратите внимание, что я также выставляю свойство из моего ChildClass
:
using System;
using System.Collections.Generic;
class DataClass : IFieldExposer<DataClass>
{
public string PropertyOne { get; set; }
public string PropertyTwo { get; set; }
public ChildClass Child { get; set; }
public IEnumerable<Func<DataClass, string>> GetFields()
{
return new List<Func<DataClass, string>>
{
a => a.PropertyOne,
b => b.Child.PropertyThree
};
}
}
class ChildClass
{
public string PropertyThree { get; set; }
}
Я также создал методы расширения для IFieldExposer<T>
, потому что я хочу сделать его простым и иметь возможность просто вызывать obj.Match(text, ignoreCase)
везде в моем коде.Этот метод должен сказать мне, если мой объект соответствует моему тексту.Вот код для ExtensionClass
, который не работает должным образом:
using System;
using System.Linq.Expressions;
using System.Reflection;
public static class ExtensionClass
{
public static bool Match<T>(this IFieldExposer<T> obj, string text, bool ignoreCase)
{
Func<bool> expression = Expression.Lambda<Func<bool>>(obj.CreateExpressionTree(text, ignoreCase)).Compile();
return expression();
}
private static Expression CreateExpressionTree<T>(this IFieldExposer<T> obj, string text, bool ignoreCase)
{
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
var exposedFields = obj.GetFields();
if (ignoreCase)
{
// How should I do convert these to lower too?
// exposedFields = exposedFields.Select(e => e.???.ToLower());
text = text.ToLower();
}
Expression textExp = Expression.Constant(text);
Expression orExpressions = Expression.Constant(false);
foreach (var field in exposedFields)
{
//How should I call the contains method on the string field?
Expression fieldExpression = Expression.Lambda<Func<string>>(Expression.Call(Expression.Constant(obj), field.Method)); //this doesn't work
Expression contains = Expression.Call(fieldExpression, containsMethod, textExp);
orExpressions = Expression.Or(orExpressions, contains);
}
return orExpressions;
}
}
Пожалуйста, проверьте комментарии в коде выше.Я хотел бы знать, как преобразовать все мои строковые свойства в нижний регистр (при желании) и как вызвать string.Contains
в каждом из них.Я получаю эту ошибку при создании своего fieldExpression
:
Method 'System.String <GetFields>b__12_0(DataClass)' declared on type 'DataClass+<>c' cannot be called with instance of type 'DataClass'
У меня нет опыта работы с деревьями выражений.Я часами читал документы и другие ответы на подобные вопросы, но все еще не могу понять, как добиться того, чего я хочу ... Я понятия не имею, что делать сейчас.
Я тестирую это вконсольное приложение, так что вот основной класс, если вы хотите создать его самостоятельно:
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var data = new DataClass
{
PropertyOne = "Lorem",
PropertyTwo = "Ipsum",
Child = new ChildClass
{
PropertyThree = "Dolor"
}
};
var dataList = new List<DataClass> { data };
var results = dataList.Where(d => d.Match("dolor", true));
}
}
РЕДАКТИРОВАТЬ
Я забыл упомянуть, что мой dataList
должен быть IQueryable
, и я хочувыполнить мой код в SQL, поэтому я пытаюсь построить деревья выражений самостоятельно.Итак, похоже, мой пример кода должен быть:
var dataList = new List<DataClass> { data };
var query = dataList.AsQueryable();
var results = query.Where(ExtensionClass.Match<DataClass>("lorem dolor"));
, в то время как мой метод становится: (Я слежу за ответом @ sjb-sjb и изменил метод GetFields()
в IFieldExposer<T>
на свойство SelectedFields
)
public static Expression<Func<T, bool>> Match<T>(string text, bool ignoreCase) where T : IFieldExposer<T>
{
ParameterExpression parameter = Expression.Parameter(typeof(T), "obj");
MemberExpression selectedFieldsExp = Expression.Property(parameter, "SelectedFields");
LambdaExpression lambda = Expression.Lambda(selectedFieldsExp, parameter).Compile();
[...]
}
А потом кажется, что мне нужно динамически звонить selectedFieldsExp
с Expression.Lambda
.Я придумал:
Expression.Lambda(selectedFieldsExp, parameter).Compile();
, и это работает, но я не знаю, как правильно вызвать DynamicInvoke()
для лямбда-выражения.
Выдает Parameter count mismatch.
, если я звонюэто без параметров и Object of type 'System.Linq.Expressions.TypedParameterExpression' cannot be converted to type 'DataClass'.
если я делаю DynamicInvoke(parameter).
Есть идеи?