【问题标题】:Shuffle list of products while querying查询时随机播放产品列表
【发布时间】:2023-04-01 15:28:01
【问题描述】:

我在数据库中有一个 Products 和 Category 表:

产品具有以下列: ProductId、ProductName、描述、路径、名称、URL、CategoryId、活动

类别具有以下列: 类别 ID、类别名称

我编写了一个 LINQ 语句来查询数据库并获取相关类别的产品。查询工作正常。

public IQueryable<Product> GetProducts(
                     [QueryString("id")] int? categoryId,
                     [RouteData] string categoryName)
{
    var _db = new products.Models.ProductContext();
    IQueryable<Product> query = _db.Products;

    if (categoryId.HasValue && categoryId > 0)
    {
        query = query.Where(p => p.CategoryID == categoryId);
    }

    if (!String.IsNullOrEmpty(categoryName))
    {
        query = query.Where(p =>
                            String.Compare(p.Category.CategoryName,
                            categoryName) == 0);
    }
    return query;
}

我有两个疑问:

  1. 如何修改此查询以仅选择 Active = 1 的那些产品。

  2. 目前,查询以我认为基于 productId - 1、2、3、4、5 等的特定顺序返回行。如何对结果进行随机排序,以便不以特定顺序返回结果.它可以是 5、2、4、1、3 或下一次运行 3、5、1、4、2 等等。

【问题讨论】:

  • 是否返回IQueryable&lt;Product&gt; 是一个要求,例如,是否允许更改方法的签名以返回IEnumerable&lt;Product&gt;?如果不是,是否允许在方法内部实现查询?
  • 如果我将其更改为 IEnumerable,它将如何受益/解决我的要求?
  • 受益 - 不,解决 - 是(第二部分 - 随机化)。看看现在的答案。我认为没有办法使用IQueryable 进行随机化。这就是我们需要具体化(即执行)查询然后随机化的原因。当然,我们总是可以将 enumerable 变成 queryable,但这会扼杀对 db 的 queryable 的整个想法。
  • 保留签名并添加p.Active == 1 标准可能会更好。通过这种方式,您可以使用该函数并应用“Count()”或添加条件或选择等。并创建另一个进行随机化的通用方法。听起来不错?

标签: c# linq


【解决方案1】:

首先,洗牌部分。用IQueryable做不到,所以最好引入一个通用的扩展方法是一些常见的地方:

public static void Swap<T>(ref T x, ref T y)
{
    var temp = x;
    x = y;
    y = temp;
}
public static T[] RandomShuffle<T>(this IEnumerable<T> source)
{
    var result = source.ToArray();
    if (result.Length < 2) return result;
    var random = new Random();
    for (int i = result.Length - 1; i > 0; i--)
    {
        int pos = random.Next(i + 1);
        if (pos != i) Swap(ref result[pos], ref result[i]);
    }
    return result;
} 

现在你的功能:

public IQueryable<Product> GetProducts(
                     [QueryString("id")] int? categoryId,
                     [RouteData] string categoryName)
{
    var _db = new products.Models.ProductContext();
    IQueryable<Product> query = _db.Products;
    query = query.Where(p => p.Active == 1); // the first question
    if (categoryId.HasValue && categoryId > 0)
    {
        query = query.Where(p => p.CategoryID == categoryId);
    }
    if (!String.IsNullOrEmpty(categoryName))
    {
        query = query.Where(p =>
                            String.Compare(p.Category.CategoryName,
                            categoryName) == 0);
    }
    return query;
}

您可以保持这种方式(IMO 更好),并在您需要该行为时在调用端应用 shuffle,例如 GetProducts(....).RandomShuffle()。或者,如果您坚持在内部进行,只需将最后一行更改为return query.RandomShuffle().AsQueryable();

【讨论】:

  • 我不确定发生了什么。我正在使用这样的 GetProducts 。我可以调试并看到行被打乱了,但是当它出现在 ListView 上时,它们看起来是有序的。
  • 我不熟悉那个控件,我猜它可能在内部应用了一些排序。
【解决方案2】:

要过滤有效产品,只需通过以下方式扩展查询:

query = query.Where(p => p.CategoryID == categoryId && p.Active == 1);

要随机播放结果,您必须自己编写代码。大概是这样的:

var shuffledList = new List<Product>();
var originalList = query.ToList();
var random = new Random();

while (originalList.Count() > 0) {
    var pick = random.Next(0, originalList.Count());
    var product = originalList[pick];
    shuffledList.Add(product);
    originalList.Remove(product);
}

shuffledList 将按随机顺序包含您的产品。不过,您的内存会有些负荷,因为您的内存中有两倍的产品数量。

我刚刚遇到的另一种可能性是:

var random = new Random();    
query = query.OrderBy(product => random.Next());

【讨论】:

  • 谢谢,我会试试看。在每个类别中,我有大约 30 种产品。我如何知道有多少产品被退回?
  • 只需使用query.Count()。这将实现结果,因此它将执行查询。
  • 只有在提交categoryId 时,Active 产品的扩展才会起作用。为什么不将其设为单独的Where
  • @Rawling 你能解释一下如何让它成为一个单独的 Where 吗?
  • @IWantToLearn Ivan 的答案在评论// the first question 的行上。
猜你喜欢
  • 2023-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-02
  • 2012-03-11
  • 2010-12-21
  • 1970-01-01
相关资源
最近更新 更多