2013-07-31 59 views
3

问候Overflowers的名单,生成的LINQ或条款从枚举

我工作的一个应用程序,允许用户生成自定义报告,我有一种情况,我需要生成从一个LINQ或条款枚举值列表。我遇到的问题是我无法看到生成Or子句的优雅方式。

例如:

//Enumeration of possible 'OR' conditions 
public enum Conditions 
{ 
    ByAlpha, 
    ByBeta, 
    ByGamma 
} 

//'Entity' I'm querying against. 
class ResultObject 
{ 
    public bool AlphaValue { get; set; } 
    public bool BetaValue { get; set; } 
    public bool GammaValue { get; set; } 
    public string Name { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     //Create list of desired conditions. 
     //Basically I want this to mimic the query, 
     // "Show me all of the ResultObjects where the AlphaValue is true or the GammaValue is true". 
     var conditions = new List<Conditions> 
     { 
      Conditions.ByAlpha, 
      Conditions.ByGamma 
     }; 

     //Sample collection of objects. This would normally be a collection of EF entities. 
     var sampleCollection = new List<ResultObject> 
     { 
      new ResultObject 
      { 
       Name = "Sample 1", 
       AlphaValue = true, 
       BetaValue = true, 
       GammaValue = true, 
      }, 
      new ResultObject 
      { 
       Name = "Sample 2", 
       AlphaValue = false, 
       BetaValue = false, 
       GammaValue = false, 
      }, 
      new ResultObject 
      { 
       Name = "Sample 3", 
       AlphaValue = true, 
       BetaValue = false, 
       GammaValue = true, 
      } 
     }; 

     var sampleCollectionQueryable = sampleCollection.AsQueryable(); 

     //This should filter the sampleCollection down to containing only the 
     //"Sample 3" ResultObject; instead, it filters out all of the ResultObjects. 
     var query = GenerateOrClause(sampleCollectionQueryable, conditions); 
    } 

    static IQueryable<ResultObject> GenerateOrClause(IQueryable<ResultObject> query, List<Conditions> conditions) 
    { 
     //This approach generates a series of AND statements, instead I need a series of OR statements 
     //for each condition. 
     foreach (var condition in conditions) 
     { 
      switch (condition) 
      { 
       case Conditions.ByAlpha: 
        query = query.Where(x => x.AlphaValue); 
        break; 
       case Conditions.ByBeta: 
        query = query.Where(x => x.BetaValue); 
        break; 
       case Conditions.ByGamma: 
        query = query.Where(x => x.GammaValue); 
        break; 
       default: 
        throw new ArgumentOutOfRangeException(); 
      } 
     } 

     return query; 
    } 
} 

任何想法?

+0

你就需要自己构建表达式树。如果向您的项目添加新库是一个选项,请查看[PredicateBuilder](http://www.albahari.com/nutshell/predicatebuilder.aspx),它完全符合您的需求。 –

回答

6

你应该Conditions一个Flags枚举:

[Flags] 
public enum Conditions { 
    ByNone = 0, 
    ByAlpha = 1, 
    ByBeta = 2, 
    ByGamma = 4 
} 

,改变你的ResultObject:

class ResultObject { 
    public Conditions Conditions { get; set; } 
    public string Name { get; set; } 
} 

然后你可以说:

var conditions = new List<Conditions> { Conditions.ByAlpha, Conditions.ByGamma }; 
var matches = sampleCollection 
        .Where(x => conditions.Select(c => c & x != 0).Any()); 

This is正确的设计,您正试图解决的问题。

如果由于某种原因,你需要保持当前的ResultObject,我现在会请OldResultObject为清楚起见:

class OldResultObject { 
    public bool AlphaValue { get; set; } 
    public bool BetaValue { get; set; } 
    public bool GammaValue { get; set; } 
    public string Name { get; set; } 
} 

可以很容易地预计,到一个新的ResultObject

var resultObject = new ResultObject { 
    Conditions = 
     (oldResultObject.AlphaValue ? Conditions.ByAlpha : Conditions.ByNone) | 
     (oldResultObject.BetaValue ? Conditions.ByBeta : Conditions.ByNone) | 
     (oldResultObject.GammaValue ? Conditions.ByGamma : Conditions.ByNone), 
    Name = oldResult.Name; 
} 

所以这真的是非常你很少努力重新设计。

0

。IEnumerable < T>上的扩展方法,它使用Func类型的谓词。为什么不创建一个列表< Func < T,bool >>你的谓词如x => x.AlphaValue,并且当你有这个列表时,迭代它并将值传递给你的IEnumerable中的一个.Where?

希望我能告诉你我已铭记在心......

+0

在这种情况下这是不正确的。 'IQueryable 。其中'需要'表达式'而不是'Func ''。 –

+0

@SimonBelanger好吧你是对的,我在我脑海中有IEnumerable,但问题是关于IQueryable ... – Jeroen1984

2

你也很可能使用一个联盟,如果你不想有改变你有非常多的代码:

static IQueryable<ResultObject> GenerateOrClause(IQueryable<ResultObject> query, List<Conditions> conditions) 
{ 
    if(conditions.Count == 0) 
     return query; 

    var resultQuery = new List<ResultObject>().AsQueryable(); 

    foreach (var condition in conditions) 
    { 
     switch (condition) 
     { 
      case Conditions.ByAlpha: 
       resultQuery = resultQuery.Union(query.Where(x => x.AlphaValue)); 
       break; 
      case Conditions.ByBeta: 
       resultQuery = resultQuery.Union(query.Where(x => x.BetaValue)); 
       break; 
      case Conditions.ByGamma: 
       resultQuery = resultQuery.Union(query.Where(x => x.GammaValue)); 
       break; 
      default: 
       throw new ArgumentOutOfRangeException(); 
     } 
    } 

    return resultQuery; 
} 
1

这里是一个解决方案使用表达式树:

private IQueryable<ResultObject> GenerateOrClause(IQueryable<ResultObject> query, 
                IList<Conditions> conditions) 
{ 
    if(conditions.Count == 0) 
     return query; 

    var pe = Expression.Parameter(typeof(ResultObject), "c"); 
    Expression builder = GetProperty(pe, conditions.First()); 

    foreach(var condition in conditions.Skip(1)) 
    { 
     var property = GetProperty(pe, condition); 
     builder = Expression.OrElse(builder, property); 
    } 

    var predicate = Expression.Lambda(builder, pe); 

    return query.Where((Func<ResultObject, bool>)predicate.Compile()).AsQueryable(); 
} 

private static MemberExpression GetProperty(ParameterExpression pe, 
              Conditions condition) 
{ 
    MemberExpression property; 

    switch (condition) 
    { 
     case Conditions.ByAlpha: 
      property = Expression.Property(pe, "AlphaValue"); 
      break; 
     case Conditions.ByBeta: 
      property = Expression.Property(pe, "BetaValue"); 
      break; 
     case Conditions.ByGamma: 
      property = Expression.Property(pe, "GammaValue"); 
      break; 
     default: 
      throw new ArgumentOutOfRangeException(); 
    } 

    return property; 
} 

我会建议使用Jason的Flags解决方案。

这里是产生的谓词的示例:

GenerateOrClause(Enumerable.Empty<ResultObject>().AsQueryable(), 
       new List<Conditions> { Conditions.ByAlpha }); 
// (c.AlphaValue) 

GenerateOrClause(Enumerable.Empty<ResultObject>().AsQueryable(), 
       new List<Conditions> { Conditions.ByAlpha, Conditions.ByBeta }); 
// (c.AlphaValue OrElse c.BetaValue) 

GenerateOrClause(Enumerable.Empty<ResultObject>().AsQueryable(), 
       new List<Conditions> { Conditions.ByAlpha, Conditions.ByBeta, Conditions.ByGamma }); 
// ((c.AlphaValue OrElse c.BetaValue) OrElse c.GammaValue)