Domanda

Does anyone know how to select a random number of records in NHibernate using LINQ.

I hoped I could say something like:

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

However it doesn't like variables in the OrderBy expression. One option is to call ToList before I do the ordering but this grabs the whole record set which is not ideal as it could return thousands of records.

I've also discovered the following (scroll down to the bottom answer):

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

However I'm not sure how I would call this using LINQ. I'd appreciate it if someone could help. Thanks

È stato utile?

Soluzione

This solution is probably not very good because it changes the shape of your entities, but if that's acceptable for your use case...

Based on this post by Ayende it looks like it's pretty easy to map SQL functions to properties on your entities:

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

Could you add a property mapping like

<property name='Random' formula='NEWID()'/>

for the entity you are targeting? Then you should be able to write a query like

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

Altri suggerimenti

To do this with NHibernate you need to add the following class ...

public class RandomOrder : Order
{
   public RandomOrder()
      : base(String.Empty, true)
   { }

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

And the following extension method ...

public static IQueryOver<TRoot, TSubType> OrderByRandom<TRoot, TSubType>(this IQueryOver<TRoot, TSubType> query)
{
   query.UnderlyingCriteria.AddOrder(new RandomOrder());

   return query;
}

This will allow you to do the following ...

var test = session.QueryOver<Entity>().OrderByRandom().Take(5).ToList();


To do this with NHibernate FluentMapping and Linq to NHibernate ...

Add a new property to your class

public virtual string Random { get; set; }

Then add the flowing mapping to your ClassMap<T>

Map(o => o.Random).Formula("NEWID()");

Finally you can call it via the following

ctx.Query<T>().OrderBy(o => o.Random).Take(5).ToList();


And to do this with EF you need to add the following method to your DataContext class ...

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

then you can call it by doing the following ...

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

which will give you the following results ...

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

Actually there is a way to call custom sql function in LINQ IQueryable:

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");
    }
}

Then you have to create custom SQL Function called Random2. For example in PostgreSQL it will be:

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

And finally you can call it in OrderBy LINQ extension

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

As a result the following sql query is going to be generated:

SELECT ... FROM Sample s order by Random2(s.Id) asc;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top