具有多个嵌套属性的 Linq 表达式
Linq Expression with multiple nested properties
我已通读问题并回答,。它看起来确实非常相似,尽管对表达式缺乏理解导致我无法将答案转换为我自己的场景。
给定一个看起来有点像这样的 class 结构:
public class Parent
{
public Parent()
{
ParentField = new HashSet<ParentField>();
}
public int ParentId { get; set; }
public ICollection<ParentField> ParentField { get; set; }
}
public class ParentField
{
public int ParentField { get; set; }
public Field Field { get; set; }
public string Value {get;set;}
}
public class Field
{
public int FieldId { get; set; }
public string Name { get; set; }
}
我正在尝试构建一个将由以下内容表示的查询:
var query = _unitOfWork.ParentRepository.Queryable().Include("ParentField.Field");
query = query.Where(i =>
(
i.ParentField.Any(pField => pField.Field.Name.Equals("anId", StringComparison.OrdinalIgnoreCase)) &&
i.ParentField.Any(pField =>
// There may be multiple values to search for, so require the OR between each value
pField.Value.Equals("10", StringComparison.OrdinalIgnoreCase) || pField.Value.Equals("20", StringComparison.OrdinalIgnoreCase))
)
// There may be multiple Names to search for, so require the AND between each Any
&&
(
i.ParentField.Any(pField => pField.Field.Name.Equals("anotherId", StringComparison.OrdinalIgnoreCase)) &&
i.ParentField.Any(pField =>
pField.Value.Equals("50", StringComparison.OrdinalIgnoreCase) || pField.Value.Equals("60", StringComparison.OrdinalIgnoreCase))
));
需要注意的重要部分是可以搜索多个 "Field.Name",每个组中也可以搜索多个 "Values"。
鉴于我不确定从哪里开始,我无法举出很多我到目前为止尝试过的例子。
任何指点都会很棒。
我不确定,但我认为您正在寻找类似的东西。
首先,您必须将搜索到的值放入能够执行所需操作的数据类型中,在本例中为字典:
var nameValues = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
{
{ "anId" , new string[] {"10", "20"} },
{ "anotherId" , new string[] {"50", "60"} },
};
然后您首先检查该名称是否在字典中,如果是,您还要检查列出的其中一个值是否在您搜索的列表中。
var query = _unitOfWork.ParentRepository.Queryable().Include("ParentField.Field");
query = query.Where(i =>
(
i.ParentField.Any(pFieldOuter => nameValues.ContainsKey(pFieldOuter.Field.Name) ?
i.ParentField.Any(pFieldInner => nameValues[pFieldOuter.Field.Name].Contains(pFieldInner.Value, StringComparer.OrdinalIgnoreCase))
: false
)
));
但是如果您想要,所有 您搜索的名称和值都包含在内,您必须这样做。
var names = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "anId", "anotherId" };
var values = new List<List<string>>() {
new List<string> { "10", "20" },
new List<string> { "50", "60" }
};
var query = _unitOfWork.ParentRepository.Queryable().Include("ParentField.Field");
query = query.Where(i =>
(
names.All(n => i.ParentField.Any(pField => pField.Name.Equals(n, StringComparison.OrdinalIgnoreCase))) &&
values.All(l => l.Any(v => i.ParentField.Any(pField => pField.Value.Equals(v, StringComparison.OrdinalIgnoreCase))))
));
即使我确定这不是最有效的方法,它对您来说可能是一个很好的提示。
在这种特殊情况下,无需构建动态表达式谓词。 &&
可以通过链接多个 Where
和 ||
通过将值放入 IEnumerable<string>
并使用 Enumerable.Contains
.
来实现
对于单个名称/值过滤器,它将是这样的:
var name = "anId".ToLower();
var values = new List<string> { "10", "20" }.Select(v => v.ToLower());
query = query.Where(p => p.ParentField.Any(
pf => pf.Field.Name == name && values.Contains(pf.Value.ToLower())));
并且对于多个键/值对:
var filters = new Dictionary<string, List<string>>
{
{ "anId", new List<string> { "10", "20" } },
{ "anotherId", new List<string> { "50", "60" } },
};
foreach (var entry in filters)
{
var name = entry.Key.ToLower();
var values = entry.Value.Select(v => v.ToLower());
query = query.Where(p => p.ParentField.Any(
pf => pf.Field.Name == name && values.Contains(pf.Value.ToLower())));
}
@geraphl 的回答提出了相同的想法,但 w/o 考虑到 EF 查询提供程序的特定要求(没有字典方法,只有原始值列表 Contains
,没有 Equals
和 StringComparison.OrdinalIgnoreCase
用法,但 ==
和 ToLower
等)
我已通读问题并回答,
给定一个看起来有点像这样的 class 结构:
public class Parent
{
public Parent()
{
ParentField = new HashSet<ParentField>();
}
public int ParentId { get; set; }
public ICollection<ParentField> ParentField { get; set; }
}
public class ParentField
{
public int ParentField { get; set; }
public Field Field { get; set; }
public string Value {get;set;}
}
public class Field
{
public int FieldId { get; set; }
public string Name { get; set; }
}
我正在尝试构建一个将由以下内容表示的查询:
var query = _unitOfWork.ParentRepository.Queryable().Include("ParentField.Field");
query = query.Where(i =>
(
i.ParentField.Any(pField => pField.Field.Name.Equals("anId", StringComparison.OrdinalIgnoreCase)) &&
i.ParentField.Any(pField =>
// There may be multiple values to search for, so require the OR between each value
pField.Value.Equals("10", StringComparison.OrdinalIgnoreCase) || pField.Value.Equals("20", StringComparison.OrdinalIgnoreCase))
)
// There may be multiple Names to search for, so require the AND between each Any
&&
(
i.ParentField.Any(pField => pField.Field.Name.Equals("anotherId", StringComparison.OrdinalIgnoreCase)) &&
i.ParentField.Any(pField =>
pField.Value.Equals("50", StringComparison.OrdinalIgnoreCase) || pField.Value.Equals("60", StringComparison.OrdinalIgnoreCase))
));
需要注意的重要部分是可以搜索多个 "Field.Name",每个组中也可以搜索多个 "Values"。
鉴于我不确定从哪里开始,我无法举出很多我到目前为止尝试过的例子。
任何指点都会很棒。
我不确定,但我认为您正在寻找类似的东西。
首先,您必须将搜索到的值放入能够执行所需操作的数据类型中,在本例中为字典:
var nameValues = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase)
{
{ "anId" , new string[] {"10", "20"} },
{ "anotherId" , new string[] {"50", "60"} },
};
然后您首先检查该名称是否在字典中,如果是,您还要检查列出的其中一个值是否在您搜索的列表中。
var query = _unitOfWork.ParentRepository.Queryable().Include("ParentField.Field");
query = query.Where(i =>
(
i.ParentField.Any(pFieldOuter => nameValues.ContainsKey(pFieldOuter.Field.Name) ?
i.ParentField.Any(pFieldInner => nameValues[pFieldOuter.Field.Name].Contains(pFieldInner.Value, StringComparer.OrdinalIgnoreCase))
: false
)
));
但是如果您想要,所有 您搜索的名称和值都包含在内,您必须这样做。
var names = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "anId", "anotherId" };
var values = new List<List<string>>() {
new List<string> { "10", "20" },
new List<string> { "50", "60" }
};
var query = _unitOfWork.ParentRepository.Queryable().Include("ParentField.Field");
query = query.Where(i =>
(
names.All(n => i.ParentField.Any(pField => pField.Name.Equals(n, StringComparison.OrdinalIgnoreCase))) &&
values.All(l => l.Any(v => i.ParentField.Any(pField => pField.Value.Equals(v, StringComparison.OrdinalIgnoreCase))))
));
即使我确定这不是最有效的方法,它对您来说可能是一个很好的提示。
在这种特殊情况下,无需构建动态表达式谓词。 &&
可以通过链接多个 Where
和 ||
通过将值放入 IEnumerable<string>
并使用 Enumerable.Contains
.
对于单个名称/值过滤器,它将是这样的:
var name = "anId".ToLower();
var values = new List<string> { "10", "20" }.Select(v => v.ToLower());
query = query.Where(p => p.ParentField.Any(
pf => pf.Field.Name == name && values.Contains(pf.Value.ToLower())));
并且对于多个键/值对:
var filters = new Dictionary<string, List<string>>
{
{ "anId", new List<string> { "10", "20" } },
{ "anotherId", new List<string> { "50", "60" } },
};
foreach (var entry in filters)
{
var name = entry.Key.ToLower();
var values = entry.Value.Select(v => v.ToLower());
query = query.Where(p => p.ParentField.Any(
pf => pf.Field.Name == name && values.Contains(pf.Value.ToLower())));
}
@geraphl 的回答提出了相同的想法,但 w/o 考虑到 EF 查询提供程序的特定要求(没有字典方法,只有原始值列表 Contains
,没有 Equals
和 StringComparison.OrdinalIgnoreCase
用法,但 ==
和 ToLower
等)