我有一个类型为Foo
的对象列表,以及一个类型为Foo
的对象的另一个实例。我想使用linq根据实例的非null属性过滤列表。
class Foo {
public int ID;
public string Description;
public long Location;
}
Foo fooFilter = new Foo() {
ID = null,
Description = null,
Location = 1
}
List<Foo> fooList = new List<Foo>();
fooList.Add(new Foo(){ID = 1, Description = "one", Location = 1});
fooList.Add(new Foo(){ID = 2, Description = "two", Location = 0});
fooList.Add(new Foo(){ID = 3, Description = "three", Location = 1});
List<Foo> filteredFooList = fooList.Where(???);
我想以某种方式使用fooFilter
查询fooList
并填写filteredFooList
:
[
{ID = 1, Description = "one", Location = 1},
{ID = 3, Description = "three", Location = 1}
]
修改
我试图简短地提出这个问题,但我可能留下了重要的信息。在我的实际程序中,List<Foo>
是来自数据库的大量结果(超过40k条目)。我正在尝试创建一个控制器方法(MVC),它可以采用与实体框架对象的字段名称匹配的任何参数组合。所以<Foo>
是EF记录类型。所以我试图避免必须明确列出可以在控制器中过滤的所有(15个左右)字段:
public class Home : Controller
{
public ActionResult FilteredFooList(int ID, string Description, long Location, etc, etc, etc)
{
}
}
做更多的事情:
public class Home : Controller
{
public ActionResult FilteredFooList(Foo filterObj)
{
}
}
也许这不可能或不是一个好主意?
答案 0 :(得分:2)
如果您只想过滤掉非实例属性,则不需要过滤器。
class Foo
{
public int ID;
public string Description;
public long Location;
public bool IsInstanciated()
{
return this.ID != default(int) && this.Description != default(string) && this.Location != default(long);
}
}
List<Foo> filteredFooList = fooList.Where(f => f.IsInstanciated());
<强> 编辑: 强>
如果你真的需要使用那个instanciated类作为过滤器,我建议你使用IEquatable<T>
class Foo : IEquatable<Foo>
{
public int ID;
public string Description;
public long Location;
public bool Equals(Foo other)
{
// Whatever your logic is
return string.IsNullOrEmpty(this.Description) == string.IsNullOrEmpty(other.Description) &&
this.ID > 0 == other.ID > 0 &&
this.Location > 0 == other.Location > 0;
}
}
public class Home : Controller
{
public ActionResult FilteredFooList(Foo filterObj)
{
List<Foo> filteredFooList = fooList.Where(f => f.Equals(filterObj));
}
}
答案 1 :(得分:1)
不要通过向数据对象添加不必要的属性来使自己陷入困境,他们应该保留数据对象。您有效地尝试构建动态查询,您希望通过条件列表有条件地过滤。有这样的模式。
从基本查询开始,然后确定是否要按其中一个属性进行过滤。对其他属性执行相同操作。当你到达终点时,你可以收集结果。
var filter = new Foo
{
ID = null,
Description = null,
Location = 1,
};
var data = new List<Foo>
{
new Foo { ID = 1, Description = "one", Location = 1 },
new Foo { ID = 2, Description = "two", Location = 0 },
new Foo { ID = 3, Description = "three", Location = 1 },
};
var query = data.AsEnumerable();
if (filter.ID != null)
query = query.Where(x => x.ID == filter.ID);
if (filter.Description != null)
query = query.Where(x => x.Description == filter.Description);
if (filter.Location != null)
query = query.Where(x => x.Location == filter.Location);
var result = query.ToList();
这假设ID
和Location
实际上可以为空,就像您的示例所暗示的那样。
public class Foo
{
public int? ID { get; set; }
public string Description { get; set; }
public long? Location { get; set; }
}
答案 2 :(得分:0)
假设您可以将值类型更改为可为空:
class Foo {
public int? ID;
public string Description;
public long? Location;
}
然后你可以使用一些扩展名:
public static class Ext {
public static bool EqualOrNull<T>(this T? value, T? filter) where T : struct, IComparable {
return (filter == null) || (value.Value.CompareTo(filter.Value) == 0);
}
public static bool EqualOrNull<T>(this T value, T filter) where T : class, IComparable {
return (filter == null) || (value.CompareTo(filter) == 0);
}
}
要做到这一点:
var filteredFooList = fooList.Where(f => f.ID.EqualOrNull(fooFilter.ID) && f.Description.EqualOrNull(fooFilter.Description) && f.Location.EqualOrNull(fooFilter.Location));
如果你想要一些真正通用的东西(例如,不依赖于知道字段名称),你需要进入反思世界。
答案 3 :(得分:0)
通过查看您的预期输出,您似乎希望过滤fooList
,以便获得与Location
对象具有相同fooFilter
的所有项目。如果那就是你要问的,你可以这样做:
List<Foo> filteredFooList = fooList.Where(item => item.Location == fooFilter.Location);
答案 4 :(得分:0)
我最近通过使用.net中主要使用Expression
类的输入对象动态构建lambda查询来解决一个非常类似的问题。如果你对你的情况感兴趣,我可以写出一个解决方案,因为已经接受了答案,但指出了可能的替代方案,因此要小心谨慎。