2017-05-15 45 views
0

我有一个Foo类型的对象的列表,以及Foo类型的对象的另一个实例。我想用linq来根据实例的非null属性过滤列表。Linq:基于类型A的对象的类型A的过滤器列表?

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

你的fooFilter不是一个过滤器,它是一个Foo对象。过滤器谓词是一个表达式,其值为true或false,对吗? – elgonzo

+1

'.ID'和'.Location'不能为null,因为它们是值类型。你打算如何处理值类型? – NetMage

+0

确实,'fooFilter'是一个考试程序,用于通过示例进行查询,例如System.DirectoryServices.AccountManagement.PrincipalSearcher如何工作。 – NetMage

回答

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(); 

这假定IDLocation实际上可为空的,就像你的榜样暗示。

public class Foo 
{ 
    public int? ID { get; set; } 
    public string Description { get; set; } 
    public long? Location { get; set; } 
} 
+0

In我的情况,'列表'实际上是一个很大的数据库列表(成千上万)。这仍然适合吗? – AnalogWeapon

+0

是的绝对,甚至更好,因为您可以构建查询并在服务器上运行,而不是将所有内容都列为列表,然后_过滤。一般来说,您应该尽可能将其构造为在服务器上运行。 –

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)); 

如果你想一些真正通用的东西(例如,不需要知道字段名称),您需要进入Refl的世界挠度。

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()); 

编辑: 如果你真的需要使用类实例化作为一个过滤器,我建议你使用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

你可能想提一下,在这种情况下,还应该*实现'Equals(object)'方法,以避免在调用Foo.Equals((object)OtherFoo)时产生意外结果等。 –

0

通过看你的预期输出,它似乎您要过滤fooList,以便您获取与您的fooFilter对象具有相同Location的所有项目。如果这是你在问什么,你可以这样做:

List<Foo> filteredFooList = fooList.Where(item => item.Location == fooFilter.Location); 
0

我最近通过基于使用.NET主要Expression类输入对象的动态建立的拉姆达查询解决了一个非常类似的问题。如果你对你的案例感兴趣,我可以写出一个解决方案,只是谨慎地这样做,因为已经有一个答案被接受,但指出了潜在的选择。

相关问题