Question

Come across a problem with the repository pattern combined with the use of abstract classes.

I have a repository which implements a single method returning an ICollection of an abstract type.

Here's my abstract class:

public abstract class Location
{
   public abstract string Name { get; set; }
   public abstract LocationType Type { get; }
}

Here's a concrete implementation of that abstract class:

public class Country : Location
{
   public override string Name { get; set; }
   public override LocationType Type { get { return LocationType.Country; } }
}

Here's my repository:

public class LocationsRepository : Locations.Repository.ILocationsRepository
{
   public ICollection<Location> GetAllLocations()
   {
      Country america = new Country { Name = "United States" };
      Country australia = new Country { Name = "Australia" };
      State california = new State { Name = "California", Country = america };

      return new List<Location>() { america, australia, california };
    }
}

All good so far.

Now the service:

public class CountryService : ICountryService
{
   private ILocationsRepository repository;

   public CountryService()
   {
      // in reality this is done by DI, but made 'greedy' for simplicity.
      this.repository = new LocationsRepository();
   }

   public List<Country> GetAllCountries()
   {
      // errors thrown by compiler
      return repository.GetAllLocations()
                       .Where(l => l.Type == LocationType.Country)
                       .ToList<Country>();
   }
}

There's the problem. I'm trying to return a list of concrete types (Country) from a repository which returns an ICollection<T> of an abstract type.

Get 2 compile-time errors:

'System.Collections.Generic.IEnumerable' does not contain a definition for 'ToList' and the best extension method overload 'System.Linq.ParallelEnumerable.ToList(System.Linq.ParallelQuery)' has some invalid arguments

and

Instance argument: cannot convert from 'System.Collections.Generic.IEnumerable' to 'System.Linq.ParallelQuery'

So, how can i implement this pattern?

I can kind of understand the issue (you can't instantiate an abstract type), so does the Enumerator (.ToList) attempt to instantiate this, hence the error?

In case you don't understand what im trying to do:

  • I want my repository to return an ICollection<T> of an abstract type
  • I want my services (i will have one for each concrete type) to return a List of concrete types based on that single repository method

Is this just a case of LINQ syntax? Or is my design pattern totally wrong?

Was it helpful?

Solution

repository.GetAllLocations().OfType<Country>().ToList();

And you don't even need the LocationType enum

OTHER TIPS

The solution to your problem is pretty easy, you need to create a new Country in your LINQ expression:

return repository.GetAllLocations()
    .Where(l => l.Type == LocationType.Country)
    .Select(l => l as Country).ToList();

I think you were mistaking the generic ToList<T> method to being able to create a list of a new type, wheres T is always inferred from the source collection. Generally, whenever you want to convert a collection of one type to a collection of items of another type you use Select.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top