Реальная проблема, которую я пытаюсь решить: у меня есть база данных с кучей телефонных номеров, которые хранятся в виде строк в абсолютно ужасном формате (например, "(02) 9971 1209"). Пользователь моей программы начнет набирать телефонный номер, и по мере того, как он печатает, то, что он печатает, будет отсылаться и использоваться для фильтрации списка телефонных номеров в базе данных с помощью операции «старт с».
Проблема в том, что если пользователь введет «02997», он не будет ничего совпадать, потому что числа хранятся с кодом местоположения, заключенным в скобки. Чтобы эта операция работала, пользователь должен будет начать каждый поиск, набрав '('. Это не хорошо.
tl; dr версия внизу вопроса
Я вынул проблему из контекста реального мира и превратился в меньшее, запечатанное решение, где я могу сосредоточиться на ней, не отвлекаясь от массивной кодовой базы. В таких условиях моим решением было сделать несколько хитростей:
class Program
{
static string [] phones = {"(02) 9489 3048","(04) 1128 2148","(01) 9971 1208",};
static void Main(string[] args)
{
for (;;)
{
Console.WriteLine("Enter your number: ");
string input = Console.ReadLine();
Func<string, string> strip =
tehstring => tehstring.Where(x => char.IsDigit(x))
.Aggregate("", (x, y) => x + y);
var results = phones.Where(z => strip(z).StartsWith(strip(input)));
foreach (var x in results)
Console.WriteLine(x);
}
}
}
Это все хорошо, отлично работает, делает именно то, что мне нужно. Но это работает только в этом изолированном контексте: я не могу перенести его обратно в кодовую базу, где он должен быть реализован, потому что мне нужно перевести его в дерево выражений.
Код, где мне нужно разбить его:
static Expression GetOperationExpression(
StringFilterOperation operation,
Expression propertyExpression,
Expression valueExpression)
{
switch (operation)
{
case StringFilterOperation.StartsWith:
var startsWith = typeof(string)
.GetMethod("StartsWith", new Type[] { typeof(string) });
Contract.Assume(startsWith != null);
return Expression.Call(propertyExpression, startsWith, valueExpression);
//etc etc etc
Мне нужно взять propertyExpression и каким-то образом встроить свойство, о котором идет речь (в данном случае это таблица телефонных номеров во внешней базе данных), в мою функцию lambda. Затем мне нужно декомпилировать все это обратно в дерево выражений и вернуть его.
Вот где я застрял. Лучшее, что я могу придумать, - это супер длинная и не элегантная строка linq, которая в любом случае даже не имеет нужного типа:
Expression<Func<string[],string, IEnumerable<string>>> expr =
(db, uinput) => db.Where(z => z.Where(x => char.IsDigit(x))
.Aggregate("", (x, y) => x + y)
.StartsWith(uinput.Where(x => char.IsDigit(x))
.Aggregate("",(x,y) => x + y)));
Я хотел сделать что-то подобное, но это не решает проблему, но немного аккуратнее:
Expression<Func<string, string>> strip =
tehstring => tehstring.Where(x => char.IsDigit(x))
.Aggregate("", (x, y) => x + y);
Expression<Func<string, string, string>> query =
(db, uinput) => db.Where(z => strip(z)
.StartsWith(strip(uinput)));
Я пытался применить к нему множество небольших изменений, но он отказывается компилироваться.
ТЛ; др:
Короче говоря, мне нужно взять выражение, представляющее поле таблицы в базе данных, и преобразовать это выражение в выражение, представляющее ту же вещь, но с примененным фильтром, который удаляет все символы, которые не являются цифры.