NHibernate - 使用 LINQ 选择随机记录数

Posted

技术标签:

【中文标题】NHibernate - 使用 LINQ 选择随机记录数【英文标题】:NHibernate - Select Random Number of Records with LINQ 【发布时间】:2013-07-22 19:44:36 【问题描述】:

有谁知道如何使用 LINQ 在 NHibernate 中随机选择记录数。

我希望我可以这样说:

var rand = new Random();
var test = session.Query<Entity>().OrderBy(x => rand.Next()).Take(5).ToList();

但是它不喜欢 OrderBy 表达式中的变量。一种选择是在我进行排序之前调用 ToList,但这会抓取整个记录集,这并不理想,因为它可能会返回数千条记录。

我还发现了以下内容(向下滚动到底部答案):

NHibernate Insert into ... select ... with GUID as PrimaryKey

但是我不确定如何使用 LINQ 来调用它。如果有人可以提供帮助,我将不胜感激。谢谢

【问题讨论】:

不太清楚你要做什么。 看起来您选择的不是随机数量的记录...相反,您选择的是 5 个随机排序的记录。您能否补充一些进一步的解释? 很抱歉给您带来了困惑。我已经更新了标题,因为它有点误导。要使用 MS SQL Server 选择随机数量的记录,您可以通过 NEWID() 排序并执行顶部操作。例如“SELECT TOP(5) * FROM Products ORDER BY NEWID()”。我正在尝试编写 LINQ 等效项。 如果Entity 表中有很多行,这种类型的查询可能会影响性能。即使您只是获取 5 条记录,SQL Server 仍然需要进行表扫描,为表中的每一行评估 NEWID() 还有RAND()函数——我不知道哪个性能更好,或者RANDs的结果是否会比NEWID更随机分布。 【参考方案1】:

此解决方案可能不是很好,因为它会改变实体的形状,但如果您的用例可以接受...

根据 Ayende 的这篇文章,将 SQL 函数映射到实体上的属性看起来很容易:

http://ayende.com/blog/1720/using-sql-functions-in-nhibernate

你能添加一个像

这样的属性映射吗

&lt;property name='Random' formula='NEWID()'/&gt;

针对您要定位的实体?然后你应该能够写一个查询像

var test = session.Query&lt;Entity&gt;().OrderBy(x =&gt; x.Random).Take(5).ToList();

【讨论】:

谢谢,这看起来很有希望。我明天会测试这个,让你知道我的发现。 NEWID() 适用于 SQL Server 但不适用于 SQL Lite - 是否有根据目标数据库进行条件映射的方法?我们使用 SQL Lite 进行单元测试 - Random() 在那里工作【参考方案2】:

要使用 NHibernate 执行此操作,您需要添加以下类 ...

public class RandomOrder : Order

   public RandomOrder()
      : base(String.Empty, true)
    

   public override SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery)
   
      return new SqlString("NEWID()");
   

还有下面的扩展方法……

public static IQueryOver<TRoot, TSubType> OrderByRandom<TRoot, TSubType>(this IQueryOver<TRoot, TSubType> query)

   query.UnderlyingCriteria.AddOrder(new RandomOrder());

   return query;

这将允许您执行以下操作...

var test = session.QueryOver&lt;Entity&gt;().OrderByRandom().Take(5).ToList();


使用 NHibernate FluentMapping 和 Linq to NHibernate 来做到这一点...

为你的类添加一个新属性

public virtual string Random get; set;

然后将流动映射添加到您的ClassMap&lt;T&gt;

Map(o =&gt; o.Random).Formula("NEWID()");

终于可以通过下面的方式调用了

ctx.Query&lt;T&gt;().OrderBy(o =&gt; o.Random).Take(5).ToList();


要使用 EF 执行此操作,您需要将以下方法添加到 DataContext 类...

[Function(Name = "NEWID", IsComposable = true)]
[return: Parameter(DbType = "uniqueidentifier")]
public Guid Random()

   return Guid.NewGuid();

然后您可以通过执行以下操作来调用它...

dc.Products.OrderBy(o => dc.Random()).Take(5)

这将为您提供以下结果...

SELECT TOP(5) * FROM Products ORDER BY NEWID()

【讨论】:

@Zote Opps 刚刚注意到,还添加了 NHibernate 版本 :) 这个解决方案肯定是针对 QueryOver 而不是 LINQ 的? +1 用于添加 NH 答案。我以前从没想过从Order 继承。【参考方案3】:

我想这就是你要找的那个,

http://puredotnetcoder.blogspot.com/2011/09/nhibernate-queryover-and-newid-or-rand.html

【讨论】:

谢谢,但这是针对 QueryOver 而不是 LINQ IQueryable。【参考方案4】:

其实在LINQ IQueryable中有一种方法可以调用自定义sql函数:

internal static class CustomLinqExtensions

    [LinqExtensionMethod]
    public static string Random2(this int input)
    
        // source: https://nhibernate.info/doc/nhibernate-reference/querylinq.html
        throw new NotImplementedException("This call should be translated to SQL and run db side, but it has run with .Net runtime");
    

然后您必须创建名为 Random2 的自定义 SQL 函数。例如在 PostgreSQL 中它将是:

CREATE OR REPLACE FUNCTION RANDOM2(in INTEGER) RETURNS DOUBLE PRECISION AS
$$
BEGIN
  RETURN random();
END;
$$
  LANGUAGE plpgsql;

最后你可以在 OrderBy LINQ 扩展中调用它

var query = CurrentSession.Query<SampleClass>()
   .OrderBy(x => x.Id.Random2());

因此将生成以下 sql 查询:

SELECT ... FROM Sample s order by Random2(s.Id) asc;

【讨论】:

以上是关于NHibernate - 使用 LINQ 选择随机记录数的主要内容,如果未能解决你的问题,请参考以下文章

通过子类结构在 nHibernate 表上使用 Linq 按类型查询

如果我使用类似 NHibernate 的 ORM,为啥需要 LINQ?

Linq to Sql vs Nhibernate vs SubSonic vs 存储过程(帮助)

NHibernate 与 LINQ(谓词?)

我在哪里可以获得用于 NHibernate 的 Linq?

如何使用 LINQ 在 NHibernate 3 中实现关键字搜索?