Beginners question regarding AsEnumerable()

Matthieu

Member
Joined
Aug 15, 2020
Messages
23
Programming Experience
Beginner
I'm following a EF Core (beginners) course and there is something I don't understand.
Below you can find the most important classes of a little excercise we got, to get to know EF core and the intent.

Student:
public class Student
    {
        public int StudentId { get; set; }
        public string Firstname { get; set; }
        public string Lastname { get; set; }

        public Student(string firstname, string lastname)
        {
            Firstname = firstname;
            Lastname = lastname;
        }
    }

EfTestDbContext:
public class EfTestDbContext : DbContext
    {
        public DbSet<Student> Students { get; set; }

        public EfTestDbContext()
        {
            EfTestInitializer.Initialize(this, true);
        }
        
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlite("Data Source = EF_TEST.db")
                    .UseLoggerFactory(LoggerFactory.Create(builder => builder.AddDebug()));
            }
        }
    }

EfTestInitializer:
public class EfTestInitializer
    {
        public static void Initialize(EfTestDbContext context, bool dropAndCreateDatabase = false)
        {
            if (dropAndCreateDatabase)
            {
                context.Database.EnsureDeleted();
            }

            if (context.Database.EnsureCreated())
            {
                Seed(context);
            }
        }
        
        private static void Seed(EfTestDbContext context)
        {
            Student s1 = new Student("M", "S");
            Student s2 = new Student("A", "S");

            context.Students.Add(s1);
            context.Students.Add(s2);

            context.SaveChanges();
        }
    }

Irepository:
public interface IRepository
    {
        IEnumerable<Student> ReadAllStudents();
    }

Repository:
 public class Repository : IRepository
    {
        private EfTestDbContext context;

        public Repository()
        {
            context = new EfTestDbContext();
        }

        public IEnumerable<Student> ReadAllStudents()
        {
            return context.Students.AsEnumerable();
        }
    }

Program:
class Program
    {
        private static readonly IRepository Repository = new Repository();
        
        static void Main(string[] args)
        {
            ShowAllStudents();
        }

        private static void ShowAllStudents()
        {
            Console.WriteLine(Repository.CountAllStudents());
        }
    }

In the body of the ReadAllStudents method under the Repository class, there is a done return of the Student DbSet with an AsEnumerable. But why should I even place AsEnumerable?
Isn't it already an Enumerable even without the AsEnumerable? with or without the result and the used query is the same...

Thanks for helping me out!
 
I would think that the AsEnumerable call actually limits the functionality available. Yes, the DbSet<Student> does implement IEnumerable<Student> but the class provides much more functionality besides. By calling AsEnumerable, you actually get a different type that provides only the specific functionality of being able to be enumerated. Basically, the caller of that code is limited to ONLY the functionality of IEnumerable<Student> and prevented from accessing the rest of the functionality of DbSet<Student>.
 
Try out your hypothesis. Does it still compile if you take it out?

If it does work, some people like being explicit, like putting private or this. even when it isn't needed. :) Seriously, though, the main job of a developer when writing code is to communicate ideas to other developers -- it just so happens that the computers can execute the steps embodied in those ideas as well.
 
I just took a look at the implementation of that method and it really does nothing. It's really just a cast:
C#:
/// <summary>Returns the input typed as <see cref="T:System.Collections.Generic.IEnumerable`1" />.</summary>
/// <param name="source">The sequence to type as <see cref="T:System.Collections.Generic.IEnumerable`1" />.</param>
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
/// <returns>The input sequence typed as <see cref="T:System.Collections.Generic.IEnumerable`1" />.</returns>
[__DynamicallyInvokable]
public static IEnumerable<TSource> AsEnumerable<TSource>(
  this IEnumerable<TSource> source)
{
  return source;
}
 
From the executable code side, you don't need it. For expressing intention, it is useful.

It's like making the intentional choice between a for, while, do-while, or a foreach loop. There is something that you want to tell the next person reading the code based on how you write your code.
 
Back
Top Bottom