У меня есть бизнес-объекты, которые выглядят следующим образом:
class Project
{
public int ID
{
get;set;
}
public string ProjectName
{
get;set;
}
public IList<ProjectTag> ProjectTags
{
get;set;
}
}
class ProjectTag
{
public int ID
{
get;set;
}
public int ProjectID
{
get;set;
}
public string Name
{
get;set;
}
public string Value
{
get;set;
}
}
Пример данных:
Project:
ID ProjectName
1 MyProject
ProjectTags:
ID ProjectID Name Value
1 1 Name 1 Value 1
2 1 Name 2 Value 2
3 1 Name 3 Value 3
По сути, это способ для наших пользователей определять свои собственные столбцы в Проекте. В результате важно помнить, что я не знаю имен записей ProjectTag во время разработки.
То, что я пытаюсь сделать, - это дать нашим пользователям возможность выбирать проекты на основе критериев поиска с использованием System.Linq.Dynamic. Например, чтобы выбрать только проект в моем примере выше, наши пользователи могут ввести это:
ProjectName == "MyProject"
Более сложным аспектом является применение фильтра к ProjectTags. В настоящее время наше приложение позволяет пользователям делать это для фильтрации проектов по их тегам Project:
ProjectTags.Any(Name == "Name 1" and Value == "Value 1")
Это работает, но начинает становиться немного грязным для конечных пользователей. В идеале я хотел бы написать что-нибудь, что позволило бы им сделать следующее:
Name 1 == "Value 1"
Или, если необходимо (из-за пробелов в имени), что-то вроде следующего ...
[Name 1] == "Value 1"
"Name 1" == "Value 1"
Из-за отсутствия лучшего объяснения мне кажется, что я хочу сделать эквивалент SQL-элемента в ProjectTags, а затем все еще иметь возможность выполнить предложение where для этого. Я рассмотрел некоторые вопросы в StackOverflow о поворотах и динамическом повороте, но я не нашел ничего слишком полезного.
Я также думал о циклическом просмотре всех имен ProjectTag и построении динамического запроса с использованием левого соединения для каждого. Я думаю, что-то вроде этого:
select
Project.*,
Name1Table.Value [Name 1],
Name2Table.Value [Name 2],
Name3Table.Value [Name 3]
from
Project
left join ProjectTag Name1Table on Name = 'Name 1'
left join ProjectTag Name2Table on Name = 'Name 2'
left join ProjectTag Name3Table on Name = 'Name 3'
А затем возьмите этот запрос и примените к нему предложение where. Но я не совсем уверен, как это сделать в Linq, а также как справиться с пробелами в имени.
Я также сталкивался с ExpandoObject. Я подумал, что, возможно, смогу конвертировать Project в ExpandoObject. Затем выполните цикл по всем известным именам ProjectTag, добавив каждое имя в ExpandoObject и, если у этого Project есть ProjectTag для этого имени, используйте это значение ProjectTag в качестве значения, иначе пустую строку. Например ...
private static object Expand(
Project project,
List<string> projectTagNames)
{
var expando = new ExpandoObject();
var dictionary = (IDictionary<string, object>) expando;
foreach (var property in project.GetType()
.GetProperties())
{
dictionary.Add(property.Name, property.GetValue(project));
}
foreach (var tagName in projectTagNames)
{
var tagValue = project.ProjectTags.SingleOrDefault(p => p.Name.Equals(tagName));
dictionary.Add(tagName, tagValue?.Value ?? "");
}
return expando;
}
Что удивительно в этом решении, так это то, что у меня есть объект, который выглядит так, как я думаю, до фильтрации с предложением where. Кажется, в имени объекта даже есть пробелы.
Затем, конечно, я обнаружил, что динамический linq плохо работает с ExpandoObject, и поэтому он не может найти динамические свойства. Я предполагаю, что это потому, что он по существу имеет тип Object, который не собирается определять какие-либо динамические свойства. Может быть, возможно создать тип во время выполнения, который соответствует? Даже если это работает, я не думаю, что это может объяснить пробелы в Имени.
Пытаюсь ли я добиться слишком многого с помощью этой функции? Должен ли я просто сказать пользователям использовать синтаксис, такой как ProjectTags.Any (Name == "Name1" и Value == "Value1")? Или есть какой-то способ обмануть динамический linq в понимании ExpandoObject? Похоже, иметь способ переопределить способ, которым динамический linq разрешает имена свойств, было бы очень удобно.