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()
函数——我不知道哪个性能更好,或者RAND
s的结果是否会比NEWID
更随机分布。
【参考方案1】:
此解决方案可能不是很好,因为它会改变实体的形状,但如果您的用例可以接受...
根据 Ayende 的这篇文章,将 SQL 函数映射到实体上的属性看起来很容易:
http://ayende.com/blog/1720/using-sql-functions-in-nhibernate
你能添加一个像
这样的属性映射吗<property name='Random' formula='NEWID()'/>
针对您要定位的实体?然后你应该能够写一个查询像
var test = session.Query<Entity>().OrderBy(x => 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<Entity>().OrderByRandom().Take(5).ToList();
使用 NHibernate FluentMapping 和 Linq to NHibernate 来做到这一点...
为你的类添加一个新属性
public virtual string Random get; set;
然后将流动映射添加到您的ClassMap<T>
Map(o => o.Random).Formula("NEWID()");
终于可以通过下面的方式调用了
ctx.Query<T>().OrderBy(o => 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?