C# Динамическое применение правила оценки к коллекции

У меня есть файл данных фиксированной длины, который необходимо сохранить в базе данных. Я использую XML-файл для определения длины полей и использую список класса FieldItem для хранения данных.

   class FieldItem
   {
        public string ObjectName {get; set;}
        public string ObjectProperty {get; set;}
        public string ObjectValue {get; set;}

        public FieldItem()
       {
       }
   }

Итак, FieldItem будет выглядеть как

var fieldItem = new FieldItem
                {
                   ObjectName = "Company",
                   ObjectProperty = "Name",
                   ObjectValue = "ABC Corp."
                }

После получения списка FieldItem я займусь созданием объекта Company и других доменных объектов, чтобы сохранить их в базе данных.

Но прежде чем они будут сохранены в базе данных, мне нужно применить некоторые бизнес-правила для проверки строки данных. Моя строка данных будет выглядеть так:

var fieldItemList = new List<FieldItem>(){
                new FieldItem {
                    ObjectName = "Company",
                    ObjectProperty = "Name",
                    ObjectValue = "ABC"
                }

                new FieldItem{
                    ObjectName = "Product",
                    ObjectProperty = "ProductCode",
                    ObjectValue  = "XYZ0123"

                    new FieldItem{
                    ObjectName = "Product",
                    ObjectProperty = "ProductName",
                    ObjectValue  = "Christmas Tree"       
                }

                    // other FieldItem objects...

            }

Например, мое правило - проверять, есть ли компания == "ABC" и ProductCode == "XYZ0123". Эти правила создаются пользователями и хранятся в базе данных в виде строки. Сейчас я использую Microsoft System.Linq.Dynamic для оценки правила, например

string validationRule = " (ObjectName == \"Company\" And ObjectProperty=\"CompanyName\" And ObjectValue = \"ABC Corp.\") And (ObjectName == \"Product\" And ObjectProperty=\"ProductCode\" And ObjectValue = \"XYZ0123\") ";

  var query = fieldItemList.AsQuerable().Where(validationRule);

затем проверьте, есть ли у него возврат одной строки, чтобы определить, прошла ли эта строка данных правило или нет. Очевидно, это слишком многословно. Есть ли у вас лучшее предложение? Что мне делать, если мне нравится только мое выражение правила, например: «Company.CompanyName = 'ABC Corp.' и Product.ProductCode = 'XYZ001' "?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
2 558
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Вы можете попросить их сохранить его в базе данных как выражение кода C#, а затем использовать классы CodeDom для его анализа и компиляции в тип, который предоставляет метод, который будет запускать сравнение.

Это создаст множество временных сборок, которыми будет PITA управлять, IMO.

Скорее, я бы по-прежнему использовал нотацию C# для синтаксиса выражения, но вместо того, чтобы компилировать ее в код, динамически создавал лямбда-выражение (через класс Expression), а затем компилировал в делегат, который принимает все параметры (вам понадобится сопоставление механизм здесь), а затем просто вызовите его, используя созданные вами типы.

Рассмотрите возможность использования библиотеки FileHelpers для анализа вашего файла непосредственно на обычные объекты домена .NET. Я считаю, что это в сочетании с вашим подходом Dynamic Linq предоставит вам синтаксис, который вы ищете.

Для вашего класса FieldItem не могли бы вы создать свойство, в которое можно было бы внедрить метод делегата Predicate, например:

class FieldItem
{
    public string ObjectName {get; set;}
    public string ObjectProperty {get; set;}
    public string ObjectValue {get; set;}
    public Predicate<FieldItem> EvalMethod { private get; set; }

    public FieldItem()
    {
    }

    public bool Evaluate()
    {
        return EvalMethod(this);
    }
}

Затем для создания объекта FieldItem вы можете использовать следующее:

new FieldItem { ObjectName = "SomeObject",
                ObjectProperty = "SomeProperty",
                ObjectValue = "SomeValue",
                EvalMethod = GetEvalMethodFor("SomeObject")
               }

Конечно, вам понадобится какой-то синтаксический анализатор (метод GetEvalMethodFor выше), чтобы преобразовать вашу строку validationRule в логический метод оценки.

Затем в своей коллекции вы можете вызвать Evaluate(), скажем, в цикле foreach.

ОБНОВИТЬ

Если вам нужно оценить каждый объект в списке, вы можете сделать это:

bool areAllItemsValid = true;

foreach (var f in fieldItemList)
{
    areAllItemsValid = areAllItemsValid && f.Evaluate();

    if (areAllItemsValid)
    {
        continue;
    }
    else
    {
        return false;
    }
}

return true;

Вы можете выбрать наследование List<FieldItem> и сделать метод EvaluateAll() таким, чтобы он содержал вышеуказанное, или вообще объявить его как метод в отдельном объекте.

Спасибо, Джон! Но мне нужно оценить список FieldItem, а не один. Другими словами, у нас есть список объектов FieldItem, мне нужно проверить некоторые из них, чтобы убедиться, что он соответствует правилам проверки.

Liang Wu 08.01.2009 08:59

Еще раз спасибо, Джон! Извините, это моя вина, я должен пояснить свой вопрос. На самом деле мне нужно более сложное правило проверки, например (A == 1 And (B == 2 OR C == 3) And (D == 4)). Я предполагаю, что динамический linq - это путь, но мне нужно правильно построить свой класс, чтобы он работал с динамическим linq.

Liang Wu 08.01.2009 19:26

Лян, в этом случае я начинаю думать, что использование объекта FieldItem, не знающего типов, может быть неправильным направлением, и может быть лучше пойти с конкретными структурами объектов. Для того, что вы описываете, очевидно, что List <T> - это представление неправильный для объекта, который вы хотите представить.

Jon Limjap 08.01.2009 19:42

Ты прав. FieldItem - это единственный временный класс для хранения данных из строки данных. Видите ли, ObjectName / Property / Value, я собираюсь выполнить рефлексию, чтобы сгенерировать реальные объекты домена для их сохранения. Эти правила меняются очень часто. Они создаются пользователями из пользовательского интерфейса.

Liang Wu 08.01.2009 22:02

В качестве отступления: почему класс вместо структуры?

Типы значений для данных, ссылочные типы для поведения.

Это актуально? Вместо этого вы должны были опубликовать это в комментариях.

Jon Limjap 08.01.2009 06:05

Я пробовал, но Stack Overflow не позволяет мне добавить комментарий, потому что у меня недостаточно «Репутации» «для комментирования требуется 50 репутации - см. Часто задаваемые вопросы»

si618 08.01.2009 07:11

После прочтения часто задаваемых вопросов самое забавное, проголосовав за меня, вы (потенциально) усложнили мне добавление комментария вместо ответа :)

si618 08.01.2009 07:14

Это описание неверно; это изменяемый POCO - он должен быть классом абсолютно, 100%. Именно очень очень редко вам нужно написать структуру в .NET - обычно для типов «меры» - например, «временной интервал» (начало / конец или начало / продолжительность) или «значение валюты» (десятичное число + код валюты). .

Marc Gravell 08.01.2009 08:01

Структуры .NET имеют очень много последствий, помимо того, что «я хочу, чтобы читатель кода понял, что я имею в виду, что это значение, а не поведение». Обращайтесь с ними очень осторожно.

Jay Bazuzi 08.01.2009 10:04

Привет, Марк, мне любопытно, почему ты такой непреклонный? Первое предложение описывает контекст как хранение данных. Ни один из кодов не выражает изменение состояния или поведения и, похоже, не нуждается в полиморфизме или допустимости значений NULL. Помимо затрат на упаковку, разве не более эффективно использовать стек и избегать сборки мусора?

si618 08.01.2009 10:12

Нет; правильное поведение более эффективно. Из-за производительности, негабаритные структуры (слишком много свойств) могут снизить эффективность уменьшать из-за того, сколько раз они закрываются (каждый вызов метода и т. д.). Но более серьезной проблемой является потеря изменений из-за семантики типа значения.

Marc Gravell 08.01.2009 12:23

Серьезно: объекты сущности домена даже если они просто хранят данные и ничего с ними не делают должны быть классами. Каждый раз. И структуры должны всегда быть неизменяемыми если вы действительно, действительно знаете, что делаете - т.е. не должны иметь никаких сеттеров (или других способов изменения состояния).

Marc Gravell 08.01.2009 12:24

Что касается «непреклонности» - потому что я видел, как слишком много людей сбиваются с толку ... они используют подход «он содержит только данные», делают его структурой, а затем задаются вопросом, почему их изменения продолжают теряться. Это может относиться к C / C++, но нет применяется к C#; struct vs class сильно отличается в C#.

Marc Gravell 08.01.2009 12:26

Спасибо, я мог бы поспорить с некоторыми из ваших пунктов, но я понимаю, откуда вы. Спасибо, что нашли время объяснить.

si618 08.01.2009 12:42
Ответ принят как подходящий

RE: «Что мне делать, если мне нравится только выражение моего правила, например:« Company.CompanyName = 'ABC Corp' и Product.ProductCode = 'XYZ001' »?


Сопоставьте этот удобный для пользователя запрос: «Company.CompanyName = 'ABC Corp' А ТАКЖЕ Product.ProductCode = 'XYZ001'» с чем-то удобным для вашей структуры данных (FieldItem):


"ObjectName = 'Company' AND ObjectProperty = 'CompanyName' AND ObjectValue = 'ABC Corp' ИЛИ ЖЕ ObjectName = 'Product' AND ObjectProperty = 'ProductCode' AND ObjectValue = 'XYZ001'»

Как узнать, соответствуют ли правила пользователя правилам или нет? Подсчитайте количество условий, если оно соответствует количеству результатов fieldItemList.AsQueryable (). Где (itemFieldFriendlyQuery), тогда строка данных действительна на основе правил пользователя.


какой-нибудь элементарный картограф (используйте регулярное выражение или сверните свой собственный синтаксический анализатор, чтобы сделать следующий код действительно действительным):

public Form3()
{
    InitializeComponent();

    string userFriendlyQuery = "Company.CompanyName = 'ABC Corp' AND Product.ProductCode = 'XYZ001'";

    string[] queryConditions = userFriendlyQuery.Split(new string[]{" AND "},StringSplitOptions.None);
    int conditionsCount = queryConditions.Length;

    string itemFieldFriendlyQuery = string.Join(" OR ", 
        queryConditions.Select(condition =>
            {
                var conditionA = condition.Split(new string[] { " = " }, StringSplitOptions.None);

                var left = conditionA[0];
                var leftA = left.Split('.');

                string objectName = leftA[0];
                string objectProperty = leftA[1];

                var right = conditionA[1];

                return string.Format("ObjectName = '{0}' AND ObjectProperty = '{1}' AND ObjectValue = {2}",
                    objectName, objectProperty, right);
            }
        ).ToArray());



    MessageBox.Show(itemFieldFriendlyQuery);

    // outputs: "ObjectName = 'Company' AND ObjectProperty = 'CompanyName' AND ObjectValue = 'ABC Corp' OR ObjectName = 'Product' AND ObjectProperty = 'ProductCode' AND ObjectValue = 'XYZ001'"


    bool isValid = fieldItemList.AsQueryable().Where(itemFieldFriendlyQuery).Count() == conditionsCount;


    MessageBox.Show(isValid.ToString());
}   

Это отличный подход! Как вы думаете, легко провести более сложное сравнение? например "(A == 1 (B == 2 OR C == 3))". Я рассматриваю возможность создания другого класса для хранения FieldItem, чтобы создать свойство как Company_Name, поэтому я могу взять Company_Name == "ABC" в динамическом предложении LINQ where

Liang Wu 08.01.2009 18:52

Другие вопросы по теме