Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using an extension method on a base class in a LINQ query

Apologies in advance for my naivety.

I am using Entity Framework to persist entities I have defined in my domain model. My domain model entities all inherit from my EntityBase class. This has properties I wish to be common to all my entities:

public class EntityBase
{
    public string CreatedBy { get; set; }
    public DateTime? Created { get; set; }

    public int ModifiedBy { get; set; }
    public DateTime? Modified { get; set; }

    public bool Enabled { get; set; }
    public bool Deleted { get; set; }
}

Now when I want to query EF using LINQ it would be nice if I didn't have to include elements to check if a particular entity is Enabled or Deleted. Every query would involve code, for example:

var messages = _db.Memberships.Where(m => m.UserId.Equals(userId))
                              .SelectMany(m => m.Group.Messages)
                              .Include(m => m.Group.Category)
                              .Select(m => m.Enabled && !m.Deleted) 
                              .ToList();

Rather than doing this each time, I thought I would write an extension method which would act on IQueryable

public static IQueryable<EntityBase> Active(this IQueryable<EntityBase> entityCollection)
    {
        return entityCollection.Where(e => e.Enabled && !e.Deleted);
    }

In my naivety I then thought I could just include this in any LINQ query which returns my entities which inherit from the EntityBase class - like so:

var messages = _db.Memberships.Where(m => m.UserId.Equals(userId))
            .SelectMany(m => m.Group.Messages)
            .Include(m => m.Group.Category)
            .Active() <============================= Extension Methd
            .ToList();

        return Mapper.Map<List<Message>,List<MessageDto>>(messages);

However, the compiler now complains that:

Error 2 Argument 1: cannot convert from
          'System.Collections.Generic.List<Diffusr.Business.Entities.EntityBase>' to
          'System.Collections.Generic.List<Diffusr.Business.Entities.Message>'  

Question : Can I achieve what I want to achieve, i.e. a common method for all my entities to return only Enabled and not Deleted? If so, how?

like image 229
jcaddy Avatar asked Mar 23 '23 02:03

jcaddy


1 Answers

Instead of specifying a concrete class, use generics, as most extension methods do:

public static IQueryable<T> Active<T>(this IQueryable<T> entityCollection) where T:EntityBase
{
    return entityCollection.Where(e => e.Enabled && !e.Deleted);
}

I assume you are using some version of .NET earlier than 4.0. Generic covariance wasn't allowed before 4.0 (ie passing an enumerable of a child type when an enumerable of the base type was expected).

Even after 4.0, it's not the absolute best idea to use covariance as the compiler ends up doing a lot of extra checks to do to ensure type safety whenever you try to store some new value to the List. Jon Skeet has a nice article about this

like image 146
Panagiotis Kanavos Avatar answered Mar 24 '23 17:03

Panagiotis Kanavos