将多个动态列汇总到子句

时间:2017-08-07 13:18:06

标签: c# mongodb linq

我有一个产品表,有5种状态,Status1,Status2,Status3,Status4。每列中的每一个代表每种状态下的库存数量。

客户已经分配了雕像,有些客户可能已经允许状态1和2,有些可能有状态2,3,4等 当客户登录时我想显示客户已分配库存状态的总和,比如说客户是否被允许状态1和3,那么我想在状态1和状态3上显示客户总和。

如果总和小于1,那么我不想显示该产品。我想在查询中执行此条件,因此每次调用我将有50(固定)产品。

如果我在视野中这样做,那么我每次通话都不会修复50个产品。

所以我的问题是如何在Where子句中动态添加列并使用条件< 1。

public int GetProductsByCategory(string productCode,string[] StockStatus)
             {
              IEnumerable<SGProduct> query = MongoContext.Products.AsQueryable<SGProduct>().AsEnumerable()
            .Where(.........);

             }

stockStatus数组包含客户允许的雕像,如Statu1,Statu2。 感谢

修改 为了简化并更好地解释它,假设我有一个方法,它接受两个参数,第一个产品代码和第二个状态(字符串数组)。

   public int GetProductsByCategory(string productCode,string[] StockStatus)
{
IEnumerable<SGProduct> query = MongoContext.Products.AsQueryable<SGProduct>().AsEnumerable()
            .Where(.........);
}

如何使Where子句动态化,所以我可以传递这样的方法:

GetProductsByCategory("ProdA",new string[] { "Status1", "Status2" };)

 GetProductsByCategory("ProdA",new string[] { "Status4", "Status7" };)

1 个答案:

答案 0 :(得分:1)

好问题!

让我们从Product实体开始:

public class Product
{
    public int Id { get; set; }
    public string ProductCode { get; set; }
    public int Status1 { get; set; }
    public int Status2 { get; set; }
    public int Status3 { get; set; }
    public int Status4 { get; set; }
    public int Status5 { get; set; }
}

我建议使用带有string[]属性的enum,而不是[Flags]来表达一组状态。

[Flags]
public enum StatusFlags
{
    None = 0,
    Status1 = 1 << 0, // 1
    Status2 = 1 << 1, // 2
    Status3 = 1 << 2, // 4
    Status4 = 1 << 3, // 8
    Status5 = 1 << 4 // 16
}

总体规划是开发Select重载public static IQueryable<TResult> Select<TResult>(this IQueryable<Product> products, StatusFlags flags, Expression<Func<Product, int, TResult>> resultSelector),让您根据产品和某些状态的总和将产品投射到某些内容中。

示例 :(给定IQueryable<Product> productsStatusFlags flags,例如StatusFlags.Status3 | StatusFlags.Status5

使用flags“ProdA”(+/-您要求的内容)总结ProductCode个产品的所有状态:

products
.Where(p => p.ProductCode == "ProdA")
.Select(flags, (p, s) => s) // resolves to .Select(p => p.Status3 + p.Status5)
.Sum()

选择IdProductCode以及每种产品所选状态的总和:

products
.Select(flags, (p, s) => new { p.Id, p.ProductCode, Status = s}) // resolves to .Select(p => new { p.Id, p.ProductCode, Status = p.Status3 + p.Status5 })

ProductCode对产品进行分组,并获取每组所选状态的总和:

products
.Select(flags, (p, s) => new { p.ProductCode, Status = s }) // resolves to .Select(p => new { p.ProductCode, Status = p.Status3 + p.Status5 })
.GroupBy(cs => cs.ProductCode, (code, css) => new { ProductCode = code, Status = css.Sum(cs => cs.Status) });

<强>实施

首先,我们需要从我的一个项目中复制出一些基础结构:一个ExpressionVisitor,用替换表达式替换表达式中的参数。

/// <summary>
/// <see cref="ExpressionVisitor"/> to replace parameters with actual expressions.
/// </summary>
public sealed class ParameterReplaceVisitor : ExpressionVisitor
{
    private readonly Dictionary<ParameterExpression, Expression> _replacements;

    /// <summary>
    /// Init with parameters and their replacements.
    /// </summary>
    public ParameterReplaceVisitor(IEnumerable<KeyValuePair<ParameterExpression, Expression>> replacements)
    {
        _replacements = replacements.ToDictionary(kv => kv.Key, kv => kv.Value);
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        Expression replacement;
        return _replacements.TryGetValue(node, out replacement) ? replacement : node;
    }
}

...etvoilà

public static class ProductExtensions
{
    private static readonly ConstantExpression _zero = Expression.Constant(0); // constant 0 for no StatusFlags

    // StatusFlag : Product => Status property of Product
    private static readonly Dictionary<StatusFlags, Expression<Func<Product, int>>> _propertySelectorsByStatus = new Dictionary<StatusFlags, Expression<Func<Product, int>>>
    {
        { StatusFlags.Status1, p => p.Status1 },
        { StatusFlags.Status2, p => p.Status2 },
        { StatusFlags.Status3, p => p.Status3 },
        { StatusFlags.Status4, p => p.Status4 },
        { StatusFlags.Status5, p => p.Status5 }
    };

    /// <summary>
    /// Project a products into a result built from the product and the sum of the Product.StatusX selected by <paramref name="flags"/>.
    /// </summary>
    /// <param name="products">The source products.</param>
    /// <param name="flags">Flags selecting the specific statuses to add up.</param>
    /// <param name="resultSelector">Expression to select the result from a product and the added statuses.</param>
    public static IQueryable<TResult> Select<TResult>(this IQueryable<Product> products, StatusFlags flags, Expression<Func<Product, int, TResult>> resultSelector)
    {
        if (products == null) throw new ArgumentNullException(nameof(products));
        if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

        var pProduct = resultSelector.Parameters[0];
        var pStatus = resultSelector.Parameters[1];
        var xStatusSum = _propertySelectorsByStatus
            .Where(kv => (kv.Key & flags) != 0) // flags has the specified status
            .Select(kv => new ParameterReplaceVisitor(new[] { new KeyValuePair<ParameterExpression, Expression>(kv.Value.Parameters[0], pProduct) }).Visit(kv.Value.Body)) // pProduct.StatusX
            .Aggregate((Expression)null, (sum, status) => sum == null ? status : Expression.Add(sum, status)) // pProduct.StatusX + pProduct.StatusY + ...
            ?? _zero; // fallback for StatusFlags.None
        var body = new ParameterReplaceVisitor(new[] { new KeyValuePair<ParameterExpression, Expression>(pStatus, xStatusSum) }).Visit(resultSelector.Body);
        var selector = Expression.Lambda<Func<Product, TResult>>(body, pProduct); // p => TResult
        return products.Select(selector);
    }
}