У меня есть файл данных фиксированной длины, который необходимо сохранить в базе данных. Я использую 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' "?





Вы можете попросить их сохранить его в базе данных как выражение кода 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() таким, чтобы он содержал вышеуказанное, или вообще объявить его как метод в отдельном объекте.
Еще раз спасибо, Джон! Извините, это моя вина, я должен пояснить свой вопрос. На самом деле мне нужно более сложное правило проверки, например (A == 1 And (B == 2 OR C == 3) And (D == 4)). Я предполагаю, что динамический linq - это путь, но мне нужно правильно построить свой класс, чтобы он работал с динамическим linq.
Лян, в этом случае я начинаю думать, что использование объекта FieldItem, не знающего типов, может быть неправильным направлением, и может быть лучше пойти с конкретными структурами объектов. Для того, что вы описываете, очевидно, что List <T> - это представление неправильный для объекта, который вы хотите представить.
Ты прав. FieldItem - это единственный временный класс для хранения данных из строки данных. Видите ли, ObjectName / Property / Value, я собираюсь выполнить рефлексию, чтобы сгенерировать реальные объекты домена для их сохранения. Эти правила меняются очень часто. Они создаются пользователями из пользовательского интерфейса.
В качестве отступления: почему класс вместо структуры?
Типы значений для данных, ссылочные типы для поведения.
Это актуально? Вместо этого вы должны были опубликовать это в комментариях.
Я пробовал, но Stack Overflow не позволяет мне добавить комментарий, потому что у меня недостаточно «Репутации» «для комментирования требуется 50 репутации - см. Часто задаваемые вопросы»
После прочтения часто задаваемых вопросов самое забавное, проголосовав за меня, вы (потенциально) усложнили мне добавление комментария вместо ответа :)
Это описание неверно; это изменяемый POCO - он должен быть классом абсолютно, 100%. Именно очень очень редко вам нужно написать структуру в .NET - обычно для типов «меры» - например, «временной интервал» (начало / конец или начало / продолжительность) или «значение валюты» (десятичное число + код валюты). .
Структуры .NET имеют очень много последствий, помимо того, что «я хочу, чтобы читатель кода понял, что я имею в виду, что это значение, а не поведение». Обращайтесь с ними очень осторожно.
Привет, Марк, мне любопытно, почему ты такой непреклонный? Первое предложение описывает контекст как хранение данных. Ни один из кодов не выражает изменение состояния или поведения и, похоже, не нуждается в полиморфизме или допустимости значений NULL. Помимо затрат на упаковку, разве не более эффективно использовать стек и избегать сборки мусора?
Нет; правильное поведение более эффективно. Из-за производительности, негабаритные структуры (слишком много свойств) могут снизить эффективность уменьшать из-за того, сколько раз они закрываются (каждый вызов метода и т. д.). Но более серьезной проблемой является потеря изменений из-за семантики типа значения.
Серьезно: объекты сущности домена даже если они просто хранят данные и ничего с ними не делают должны быть классами. Каждый раз. И структуры должны всегда быть неизменяемыми если вы действительно, действительно знаете, что делаете - т.е. не должны иметь никаких сеттеров (или других способов изменения состояния).
Что касается «непреклонности» - потому что я видел, как слишком много людей сбиваются с толку ... они используют подход «он содержит только данные», делают его структурой, а затем задаются вопросом, почему их изменения продолжают теряться. Это может относиться к C / C++, но нет применяется к C#; struct vs class сильно отличается в C#.
Спасибо, я мог бы поспорить с некоторыми из ваших пунктов, но я понимаю, откуда вы. Спасибо, что нашли время объяснить.
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
Спасибо, Джон! Но мне нужно оценить список FieldItem, а не один. Другими словами, у нас есть список объектов FieldItem, мне нужно проверить некоторые из них, чтобы убедиться, что он соответствует правилам проверки.