Home > Software engineering >  Confused About Why I have no extension methods available
Confused About Why I have no extension methods available

Time:10-20

I'm implementing a Unit OF Work Pattern (Not my choice, i know its considered by some as an anti-pattern). I've come across a situation I don't fully understand.

My Generic Repo constructor:

this.context = context;
this.dbSet = context.Set<T>();

Generic Method:

  public virtual async Task<IEnumerable<T>> All()
    {
        return await dbSet.ToListAsync();
    }

Me Using It:

  var languages = await _unitOfWork.Languages.All();
        languages = languages.OrderBy(x => x.Order);

As shown above, I need to go down a line so I can use OrderBy, I don't undestand why.

A second Question is, ToListAsync is suppose to return a list, why do I get an IENUMARABLE?

CodePudding user response:

A second Question is, ToListAsync is suppose to return a list, why do I get an IENUMARABLE?

List is an implementation of IEnumerable. Check What does it mean to "program to an interface"?.

As shown above, I need to go down a line so I can use OrderBy, I don't why.

_unitOfWork.Languages.All() returns a Task, you should get its unwrapped result of IEnumerable to apply OrderBy on it.

To make this work as you expected, you should apply OrderBy on the awaited result:

(await _unitOfWork.Languages.All()).OrderBy(x => x.Order);

CodePudding user response:

Your confusion as far as having them on the same line seems to be on how it's applying the await. This should work fine:

var languages = (await _unitOfWork.Languages.All()).OrderBy(x => x.Order);

You need to do this because your All function is returning a Task while OrderBy is not.

Additionally, you probably don't want to call .ToListAsync() in the first place, that's just a SELECT * without any limit or where clause which may not be the "end of the world" all the time but often will be extremely detrimental to your performance.

As for your second question about why it's returning an IEnumerable<T> it's because List<T> implements IEnumerable<T> and your function signature indicates that it returns IEnumerable<T>

public virtual async Task<IEnumerable<T>> All()

I highly recommend instead of ever dealing with .ToListAsync or IEnumerable<T> you leave it alone and let IQueryable<T> do its proper work against the database.

EDIT: It's been asked in comments why it matters that you'd use IQueryable<T>. The answer is because utilizing .Where, .Any() etc... against IQueryable will mold the underlying query against the DB.

For example, say you want to find a single entity with the Id of 123. If you leave your .ToListAsync() without any other modifications it will pull back every single row in the DB into your program in memory and then iterate every single row looking for that one row.

If, instead, you use IQueryable<T> - at the point where you apply .FirstOrDefault(e => e.Id == 123) it will be applied to the database and it will apply as something like SELECT TOP(1) * FROM MyEntity WHERE [Id] = 123 - pulling back a single row instead of every row in existence.

EDIT2: Note that this also applies to projection. This means something like .Select(e => new { FullName = e.FirstName " " e.LastName, State = e.Address.State}) will only pull those 2 columns instead of all columns and any nav properties you include. Doing that after a .ToList() or .ToListAsync will instead pull back all columns/entities and iterate over them to create a whole new set of other entities. This can result in MASSIVE CPU/Memory differences between the 2 approaches.

  • Related