Question

I have the standard Repository interface setup:

public interface IRepository<T, TKey> : IDisposable where T : class
{
    T Get(TKey id);
    IEnumerable<T> Get();
    IEnumerable<T> Find(Expression<Func<T, bool>> whereClause);
    T Add(T entity);
    void Delete(T entity);
    bool Save();
    int Update(T entity);
}

And there is an implementation for it:

public class EfRepository<T, TKey> : IRepository<T, TKey> where T : class
{
    protected DbContext _context;

    public EfRepository(DbContext context) { ...}

    public virtual T Get(TKey id) {...}
    public virtual IEnumerable<T> Get() { ...}

    public virtual IEnumerable<T> Find(Expression<Func<T, bool>> whereClause)  { ...}
    public T Add(T entity) { ...}
    public void Delete(T entity) { ...}
    public bool Save() { ...}
    public int Update(T entity) { ...}
}

And I have a services layer on top of all of that:

public class VehicleService: IVehicleService
{
    private readonly IRepository<Vehicle, int> _repository;

    public VehicleService(IRepository<Vehicle, int> repository)
    {
        _repository = repository;
    }

    public IEnumerable<int> GetModelYears()
    {
        // ?? help?
    }
}

My question is, where does one implement a Distinct method? A LINQ query looks like this:

context.Set<Vehicle>().Select(x => x.ModelYear).Distinct();

I haven't figured out how to generically code a Distinct method at the repository level, and I don't think that's the right place to put it.

We've chosen to not return IQueryable in our repository. But I've also not exposed any actual DbSet objects to the services layer.

Maybe a better question is, is this a correct way of doing things? Is there a better way?

Was it helpful?

Solution

If this is something that is only gonna be used for one kind of object, and not for all use in the repository, you could just make a new Repository, containing that query, but built on IRepository.

public interface IVehicleRepository : IRepository<Vehicle>{
    IEnumerable<int> GetDistinctVehicle();
}

public VehicleRepository : EfRepository<Vehicle>, IVehicleRepository{

    public VehicleRepository(DbContext context) : base(context)
    {
    }


    public IEnumerable<int> GetDistinctVehicle(){
        Context.Set<Vehicle>().Select(x=> x.ModelYear).Distinct();
    }

}

In that way, you can still use all of the IRepositorystuff, but also your extra method. Just bind IVehicleRepository to VehicleRepository in your injector of your choice and inject it into the constructor like you do with the IRepositories.

OTHER TIPS

A couple of options here:

1) The most efficient method would be to perform your

context.Set<Vehicle>().Select(x => x.ModelYear).Distinct();

section in your repository. As you may already know, this will ensure that the sql query performs the distinct at the database level, prior to returning the result set. This ensures that a large data set is not returned from your database, only to have .Net further limit the result set in memory. From there, you would have your _vehicleService.GetModelYears make a call to _vehicleRepository.GetDistinctModelYears.

2) If you are working with a very small data set and the performance hit associated with performing an in-memory Distinct function on a hand full of rows doesn't matter, you could: a) have your 'GetModelYears()' method in the Service call a _vehicleRepository.GetVehicles() method and then b) perform the Distinct on the vehicle list returned from your repository.

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