3

When using the repository pattern , sometimes you have the same logic that appears in different repositories.In the example below GetTempEmployees() in EmployeeRepository and GetCompaniesWithTemps() in CompanyRepository have the same expressions

e.IsTemp && e.IsDeleted == false

My question is what is the recommended practice for minimizing this duplication of expression logic.

eg.

public class Employee
{
    public int EmployeeId { get; set; }
    public bool IsTemp { get; set; }
    public bool IsDeleted { get; set; }
}

public class Company
{
    public int CompanyId { get; set; }
    public bool IsDeleted { get; set; }
    public virtual ICollection<Employee> Employees { get; set; } 

}

public class TestContext : DbContext
{
    public TestContext()
    {
    }
    public DbSet<Employee> Employee { get; set; }
    public DbSet<Company> Company { get; set; }
}


public class EmployeeRepository
{
    private readonly TestContext _context;
    EmployeeRepository(TestContext context)
    {
        _context = context;
    }

    public ICollection<Employee> GetTempEmployees()
    {
        return _context.Employee.Where(e => e.IsTemp && e.IsDeleted==false).ToList();
    }
}

public class CompanyRepository
{
    private readonly TestContext _context;
    CompanyRepository(TestContext context)
    {
        _context = context;
    }

    public ICollection<Company> GetCompaniesWithTemps()
    {
        return _context.Company.Where(c => c.Employees.Any(e => e.IsTemp && e.IsDeleted == false)).ToList();
    }
}
kwiri
  • 1,399
  • 2
  • 15
  • 22

2 Answers2

4

The solution proposed by Rawling will work. Another solution would be to provide extensions working over IQueryables. Something like:

static class PersonSetExtensions
{
    public static IQueryable<Person> WhereTempAndNotDeleted(this IQueryable<Person> set)
    {
        return set.Where(x => x.IsTemp && !x.IsDeleted);
    }
}

Which can be used in your code as:

return _context.Employee
               .WhereTempAndNotDeleted()
               .ToList();
Polity
  • 14,734
  • 2
  • 40
  • 40
  • This is what I suggested last time I answered a question similar to this :) – Rawling Oct 19 '12 at 08:38
  • this seems like a good solution too but how will you use this method for collection queries eg .Any((e => e.IsTemp && e.IsDeleted == false) – kwiri Oct 19 '12 at 09:15
  • Turn the expression around! `_context.Company.Where(x => x.Employees.WhereTempAndNotDeleted().Any()).ToList();` – Polity Oct 19 '12 at 09:19
  • I have tried this after changing to x => x.Employees.AsQueryable().WhereTempAndNotDeleted().Any() and I am getting error LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1 – kwiri Oct 23 '12 at 08:03
  • Because you're making a call to AsQueryable. There is no SQL representation for this call and therefore EF will fail. Remove the call since its unnecessary. – Polity Oct 23 '12 at 10:11
3

I believe you have to use an Expression, e.g.

static class EmployeeExpressions
{
    public static System.Linq.Expressions.Expression<Func<Employee, bool>>
        IsTempAndNotDeleted = e => e.IsTemp && !e.IsDeleted;
}

You can then use it as

...
return _context.Employee
    .Where(EmployeeExpressions.IsTempAndNotDeleted)
    .ToList();

...
return _context.Company
    .Where(c => c.Employees.Any(EmployeeExpressions.IsTempAndNotDeleted))
    .ToList();

but I'm a little hazy on this, so give it a try and see if it works.

Rawling
  • 49,248
  • 7
  • 89
  • 127
  • this seems interesting let me try it – kwiri Oct 19 '12 at 09:05
  • this works if use the AsQueryable extension on the collection _context.Company.Where(c => c.Employees.AsQueryable().Any(EmployeeExpressions.IsTempAndNotDeleted)).ToList(); – kwiri Oct 23 '12 at 08:12