0

I would like something like this:

    public int NumberStudent()
    {
        int i = 0;
        if (db.Tbl_Student.ToList().Count() > 0)
            i = db. Tbl_Student.Max(d => d.id);
        return i;
     }

However, I would like to use it on any table:

   public int FindMaxId(string TableName)
     {
       int i =0;
        if ('db.'+TableName+'.ToList().Count() > 0' )
           i = db. TableName.Max(d => d.id);
        return i ;
     }

I know it is wrong, but I'm not sure how to do it.

Zac Blazic
  • 607
  • 2
  • 6
  • 17
niknaz
  • 47
  • 2
  • 11
  • 2
    `.ToList().Count() > 0` could very likely be replaced with `.Any()` it will improve your performance too because it does not need to enumerate the table twice. – Scott Chamberlain Aug 30 '13 at 20:08

3 Answers3

9

You can use the IEnumerable/IQueryable extension method DefaultIfEmpty for this.

var maxId = db.Tbl_Student.Select(x => x.Id).DefaultIfEmpty(0).Max();

In general, if you do Q.DefaultIfEmpty(D), it means:

If Q isn't empty, give me Q; otherwise, give me [ D ].

Timothy Shields
  • 75,459
  • 18
  • 120
  • 173
4

Below I have written a simple wrapper around the existing Max extension method that allows you provide an empty source (the table you were talking about).

Instead of throwing an exception, it will just return the default value of zero.

Original

public static class Extensions
{
    public static int MaxId<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int> selector)
    {
        if (source.Any())
        {
            return source.Max(selector);
        }

        return 0;
    }
}

This was my attempt, which as noted by Timothy is actually quite inferior. This is because the sequence will be enumerated twice. Once when calling Any to check if the source sequence has any elements, and again when calling Max.

Improved

public static class Extensions
{
    public static int MaxId<TSource>(this IQueryable<TSource> source, Func<TSource, int> selector)
    {
        return source.Select(selector).DefaultIfEmpty(0).Max();
    }
}

This implementation uses Timothy's approach. By calling DefaultIfEmpty, we are making use of deferred execution and the sequence will only be enumerated when calling Max. In addition we are now using IQueryable instead of IEnumerable which means we don't have to enumerate the source before calling this method. As Scott said, should you need it you can create an overload that uses IEnumerable too.

In order to use the extension method, you just need to provide a delegate that returns the id of the source type, exactly the same way you would for Max.

public class Program
{
    YourContext context = new YourContext();

    public int MaxStudentId()
    {
        return context.Student.MaxId(s => s.Id);
    }

    public static void Main(string[] args)
    {
        Console.WriteLine("Max student id: {0}", MaxStudentId());
    }
}

public static class Extensions
{
    public static int MaxId<TSource>(this IQueryable<TSource> source, Func<TSource, int> selector)
    {
        return source.Select(selector).DefaultIfEmpty(0).Max();
    }
}
Community
  • 1
  • 1
Zac Blazic
  • 607
  • 2
  • 6
  • 17
  • Take a look at my answer. There's a lesser-known LINQ method for this that already exists. – Timothy Shields Aug 30 '13 at 19:48
  • Another good approach, definitely. This cat can be skinned in many ways! Upvoted :) – Zac Blazic Aug 30 '13 at 19:52
  • 1
    The point was that the body of your `MaxId` helper method could simply be replaced with `return source.Select(selector).DefaultIfEmpty(0).Max();`. Favor composing existing operations over making new ones from scratch. Your `MaxId` method would actually enumerate `source` twice. – Timothy Shields Aug 30 '13 at 19:56
  • That is quite true and thanks for the tip. I will update the snippet using your approach, if that's okay? – Zac Blazic Aug 30 '13 at 20:07
  • Perfectly fine with me. Maybe even make note of the first implementation you had and then why it's better to switch it over (enumerate source only once instead of twice). Also, you could make it an `IQueryable` extension method instead so that it can be done database-side rather than client-side. – Timothy Shields Aug 30 '13 at 20:13
  • The one last thing I would change is make two overloads One that takes in `IEnumerable` and one that takes in `IQueryable` as the IQueryable provider may result in a smaller query to the DB if it can get away with it. – Scott Chamberlain Aug 30 '13 at 20:13
  • Thanks guys. Hopefully someone will find this useful. – Zac Blazic Aug 30 '13 at 20:34
  • thanks Zac Blazic .but how can i use your class Extensions .can you make an example with one table. – niknaz Aug 31 '13 at 05:01
  • I have updated the example to more closely resemble what you want to do. – Zac Blazic Aug 31 '13 at 14:07
0

db.Tbl_Student.Aggregate(0, (maxId, s) => Math.Max(maxId, s.Id))
or
db.Tbl_Student.Max(s => (int?)s.Id) ?? 0

Vladimir
  • 7,345
  • 4
  • 34
  • 39
  • thanks. but i wanna use this for any table. i wanna pass name of table and it returns maxid. – niknaz Aug 31 '13 at 05:03