我有一个产品表,有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" };)
答案 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> products
和StatusFlags 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()
选择Id
,ProductCode
以及每种产品所选状态的总和:
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);
}
}