C# 规范模式-使用ICollection实现表达式

C# 规范模式-使用ICollection实现表达式,c#,lambda,linq-to-sql,entity-framework-6,domain-driven-design,C#,Lambda,Linq To Sql,Entity Framework 6,Domain Driven Design,我已经实现了以下特定模式 例如,我有一个产品表,它与WarehouseProducts表有多对多关系。我需要找到一个给定的磨损房屋列表的所有产品。这就是我所拥有的 public class Products { [Key] public int Id { get; set; } public string Name { get; set; } public ICollection<WarehouseProduct> Warehouses { get;

我已经实现了以下特定模式

例如,我有一个产品表,它与WarehouseProducts表有多对多关系。我需要找到一个给定的磨损房屋列表的所有产品。这就是我所拥有的

public class Products
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<WarehouseProduct> Warehouses { get; set; }
}

public class WarehouseProduct
{
    [Key]
    public int Id { get; set; }
    public int WarehouseId { get; set; }
    [ForeignKey("ProductId")]
    public int ProductId { get; set; }
}
public class WarehouseProductSpecification : Specification<Products>
{
    private readonly List<int> _ids;    
    public WarehouseProductSpecification(IEnumerable<int> warehouseIds)
    {
        _ids = warehouseIds.ToList();
    }    
    public override Expression<Func<Products, bool>> ToExpression()
    {
        Expression<Func<WarehouseProduct, bool>> expr =
            (w) => _ids.Contains(w.WarehouseId);

        return
            q => !_ids.Any()
                 || (_ids.Any() && q.Warehouses != null && q.Warehouses.Any(expr.Compile()));
    }
}
所以to列表得到了错误

更新2 我尝试使用@Evk添加的代码

if (_ids.Count == 0)
    return x => true;
return q => q.Warehouses.Any(w => _ids.Contains(w.WarehouseId));
我在尝试您的代码时遇到以下错误

    Test [0:10.297] Failed: System.ArgumentException: Property 'System.String WearhouseId' is not defined for type 'Data.TableObjects.Products'
System.ArgumentException
Property 'System.String WearhouseId' is not defined for type 'Data.TableObjects.Products'
   at System.Linq.Expressions.Expression.Property(Expression expression, PropertyInfo property)
   at System.Linq.Expressions.ExpressionVisitor.VisitArguments(IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.ExpressionVisitor.VisitArguments(IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node)
   at Infrastructure.Expressions.AndSpecification`1.ToExpression() in C:\..\Expressions\AndSpecification

您必须始终记住,表达式是通过实体框架转换为SQL查询的。例如,想想这是怎么回事

q.Warehouses.Any(expr.Compile())
可以翻译成SQL吗?它不能,因为
expr.Compile()
的结果基本上是.NET代码-它不再是表达式树。您可以使用第三方库,例如,将一个表达式集成到另一个表达式中,但如果没有第三方库,它将无法正常工作。但在您的特定情况下,这是不需要的

首先,你需要清理你的表情。如果
\u id
列表为空,则无需筛选任何内容。因此,返回仅返回true(匹配所有)的表达式:

现在,在
if
之后,我们知道列表中有ID,所以我们可以跳过所有与此相关的检查。然后,您不需要检查仓库是否为空。此检查是导致您观察到异常的原因。考虑到此表达式将转换为SQL查询,因此可以删除此检查,这是没有意义的。表达式内部的代码永远不会直接执行(至少在EF6中),所以不可能出现空引用异常

这就只剩下一个表达式,它实际上可以做有用的工作,所以最后的
ToExpression
将是:

if (_ids.Count == 0)
    return x => true;
return q => q.Warehouses.Any(w => _ids.Contains(w.WarehouseId));

你在哪一行得到了这个错误?我已经更新了问题@FaizanRabbani
ParameterReplacer
你下面的示例中的实现太天真了,显然不正确。@IvanStoev你能给我指出任何好的例子吗请
和规范
使用上面的实现:
公共重写表达式ToExpression(){var left=_left.ToExpression();var right=_right.ToExpression();return Expression.Lambda(Expression.AndAlso(left.Body,right.Body.ReplaceParameter(right.Parameters[0],left.Parameters[0])),left.Parameters[0]);}
希望能有所帮助。我添加了我在尝试您的代码时遇到的错误。AND规范似乎无法处理此问题。您能告诉我一些错误吗direction@MJK这是因为您的链接(特别是ParameterReplace)错误地实现了AndSpecification.据我所知,该代码仅用于演示目的,尚未准备好生产。我建议使用稳定库,如上面提到的LinqKit,将表达式与And和Or组合。
if (_ids.Count == 0)
   return x => true;
if (_ids.Count == 0)
    return x => true;
return q => q.Warehouses.Any(w => _ids.Contains(w.WarehouseId));