如何判断一个表达式树是否是另一个表达式树的子集

时间:2018-08-07 12:26:54

标签: c# linq iqueryable

假设我有

int id1 = 1;
int id2 = 2;
IQueryable<T> source = someSource;
IQueryable<T> predicate1 = source.Where(x => x.id1 = id1);
IQueryable<T> predicate2 = source.Where(x => x.id1 = id1 && x.id2 = id2);

如果predicate1的所有动作都在predicate2中完成,是否有一种简单的方法可以找出来?像predicate1.isChildOf(predicate2) = true and predicate2.isChildOf(predicate1) = false

我想查询数据库并将结果保存在内存中。鉴于我已经进行了没有太多限制的查询,因此可以查询它而不是数据库。

编辑: 在进行新查询时,我只有以前的数据集以及相应的谓词。假设(d(ata)1,p(redicate)1),(d2,p2)。我更喜欢从那些数据集中获取数据而不是从数据库中获取数据(较便宜),但是某些谓词过于严格,因此我需要查询原始数据库。给定一个新的谓词p3,我需要确定d1或d2是否未“过多过滤”。例如,我们具有与原始帖子和p3 = source.Where(x => x.id3 == id3)中相同的谓词。然后我仍然需要查询原始数据库。但是,如果我有p4 = source.Where(x => x.id1 = id1),那么我可以简单地取d1。

4 个答案:

答案 0 :(得分:2)

通常来说,确定两个函数在给定相同的输入(定义为这些函数“等效”)的情况下是否产生相同的值is provably impossible。因此,从一开始,您就无法按要求解决问题。您可以为某些特定情况解决问题,例如,如前所述,如果两个表达式的引用相等,那么我们知道它们将是等效的,但是您想出的任何解决方案都将具有假阳性或假阴性。 / p>

在引用相等之外,甚至尝试提出一些启发式方法,对于非常简单的情况,等效函数非常非常困难,因为有很多方法可以显示程序甚至具有至少在某些边缘条件下,最小的差异可能会具有不同的行为。

使用除分析一种方法的返回类型是否相等还是另一种方法的子类型之外的任何方法,尝试确定一个函数的理论输出值将始终是另一种函数的子集将是< em>更难。

答案 1 :(得分:0)

Servy的答案在寻址时是正确的

  

如果predicate1的所有动作都在predicate2中完成,是否有一种简便的方法来找出?

当你问

  

如何判断一个表达式树是否是另一个表达式树的子集

这很困难,但并非不可能。您可以 确定一个表达式是否是另一个表达式的子集,因为表达式是树,因此您可以遍历和比较树。

您将需要访问每个表达式的节点(顺便说一句,您需要在获取IQueryable之前对表达式进行操作,而不仅仅是在lambda上进行操作)。要进行访问,您可以使用ExpressionVisitor或通过检查表达式的类型来滚动浏览。

一个简单的算法是从每个LINQ表达式的根开始并比较其节点。如果没有匹配项,则迭代到提议的超级树的子代。如果找到匹配项,则对两棵树进行迭代。如果您对提议的子树的一部分进行迭代然后不匹配,则会有些回溯。这个page在C语言中有一个关于二叉树的示例。您将需要一个更通用的算法。

您需要考虑表达式的类型,因为这决定了哪些节点可用(例如,它们是一元表达式还是二进制表达式等)。如果要与解决方案完全通用,则需要考虑操作数的类型。

不是要考虑操作数的名称,因为在确定是否有子图时它们并不重要(例如,第一个仍然是第二个的子集) :

x => x.id1 == id1
y => y.id1 == id1 && y.id2 == id2

答案 2 :(得分:-1)

Contains

  

通过使用指定的IEqualityComparer确定序列是否包含指定的元素

ALL

  

确定序列中的所有元素是否都满足条件。

因此,如果predicate2包含predicate1的所有项目,我们可以得出结论,predicate1predicate2的子代。

predicate1.All(x=> predicate2.Contains(x)) == true  <--- predicate1 is child of predicate2

predicate2.All(x=> predicate1.Contains(x)) == false  <--- predicate2 isn't child of predicate1

您必须确保为类型IEqualityComparer定义T

答案 3 :(得分:-1)

AFAIK,您无法比较谓词的内容,只能比较谓词的引用。因此,要检查谓词是否为的子代,您不能直接对其进行检查。
但是,您可以检查List<Predicate<T>>是否包含谓词的引用

List<Predicate<T>> predicates = new List<Predicate<T>>();
Predicate<T> equalsOne = x => x == id1;
Predicate<T> equalsTwo = x => x == id2;
predicates.Add(equalsOne);
predicates.Add(equalsTwo);
IQueryable<T> predicate1 = someSource.Where(equalsOne);
IQueryable<T> predicate2 = someSource.Where(x => predicates.All(p => p(x)));

predicates 包含equalsOneequalsTwo
predicates 既不包含x => x == id1也不包含x => x == id2


如果您想使用完整的类来简化Where方法,可以使用以下方法:

public sealed class PredicateCollection<T> : ICollection<Predicate<T>>
{
    #region PredicateCollection
    private List<Predicate<T>> Predicates { get; set; }
    public PredicateCollection() { Predicates = new List<Predicate<T>>(); }
    #endregion

    #region ICollection<Predicate<T>>
    public int Count { get { return Predicates.Count; } }
    public bool IsReadOnly { get { return false; } }
    public void Add(Predicate<T> item) { Predicates.Add(item); }
    public void Clear() { Predicates.Clear(); }
    public bool Contains(Predicate<T> item) { return Predicates.Contains(item); }
    public void CopyTo(Predicate<T>[] array, int arrayIndex) { Array.Copy(Predicates.ToArray(), 0, array, arrayIndex, Count); }
    public IEnumerator<Predicate<T>> GetEnumerator() { return Predicates.GetEnumerator(); }
    public bool Remove(Predicate<T> item) { return Predicates.Remove(item); }
    IEnumerator IEnumerable.GetEnumerator() { return Predicates.GetEnumerator(); }
    #endregion

    #region PredicateCollection methods
    /// <summary>Check if any predicate is true</summary>
    public bool CheckAny(T item) { return Predicates.Any(predicate => predicate(item)); }
    /// <summary>Check if all predicates are true</summary>
    public bool CheckAll(T item) { return Predicates.All(predicate => predicate(item)); }
    /// <summary>Allow to use collection as predicate (check for all predicates)</summary>
    public static implicit operator Func<T, bool>(PredicateCollection<T> collection) { return item => collection.CheckAll(item); }
    #endregion
}

然后像这样使用它:

PredicateCollection<T> predicates = new PredicateCollection<T>();
Predicate<T> equalsOne = x => x == id1;
Predicate<T> equalsTwo = x => x == id2;
predicates.Add(equalsOne);
predicates.Add(equalsTwo);
IQueryable<T> predicate1 = someSource.Where(equalsOne);
IQueryable<T> predicate2 = someSource.Where(predicates);