views:

1081

answers:

4

I have written a function that gets a given number of random records from a list. Currently I can do something like:

IEnumerable<City> cities = db.Cites.GetRandom(5);

(where db is my DataContext connecting to a SQL Server DB)

Currently, I have a function like this in every entity I need random records from:

public partial class City
{

 public static IEnumerable<City> GetRandom(int count)
 {
  Random random = new Random();
  IEnumerable<City> cities = DB.Context.Cities.OrderBy( c => random.Next() ).Take(count);

  return cities;
 }

}

It works fine, but I'd like it to be generic, so it can work for any table, or even any list of items. I have tried an extension method like:

 public static IEnumerable<T> GetRandom<T>( this Table<T> table, int count)
 {
  Random random = new Random();
  IEnumerable<T> records = table.OrderBy(r => random.Next()).Take(count);

  return records;
 }

but I get:

  Error 1 The type 'T' must be a reference type in order to use it as parameter 'TEntity' in the generic type or method 'System.Data.Linq.Table' 

which higlights "GetRandom"

I don't understand what the problem is here. Can someone clear up the proper syntax?

A: 

Try this

public static IEnumerable<T> GetRandom<T>( this Table<T> table, int count) where T : class {
  ...
}
JaredPar
+3  A: 

JaredPar, I don't think you can do that with the :class inside the generic definition.

I think this is the proper way to define type constraints:

public static IEnumerable<T> GetRandom<T>( this Table<T> table, int count) where T : class {
  ...
}

More info on type constraints here

Strelok
You're correct, I'm doing constraints in a different language :(
JaredPar
+4  A: 

I like the idea of a random order function, and it can be applied to any IEnumerable like so:

public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
{
    Random rnd = new Random();
    return source.OrderBy(t => rnd.Next());
}

...

IEnumerable<City> cities = db.Cites.Randomize().Take(5);
Cameron MacFarland
+3  A: 

If you are talking to a database, you might want to do the random fetch at the database. With LINQ-to-SQL you can (for smallish volumes) do this via a [Function] pointing at NEWID (on the data-context):

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

then in a query via:

     ctx.Log = Console.Out; 
     var query = from x in ctx.Suppliers 
                 orderby ctx.Random() 
                 select x; 
     var results = query.ToArray();

which gives TSQL (here using Northwind):

SELECT [t0].[SupplierID], [t0].[CompanyName], [t0].[ContactName], 
[t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], 
[t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax], [t0].[HomePage] 
FROM [dbo].[Suppliers] AS [t0] 
ORDER BY NEWID()

You can obviously mix this with Take(1) etc.

It doesn't work in entity framework, though.

Marc Gravell