The Story So Far....
I'm learning MVC(4) at the moment. I've written my app which is full of Controller Bloat so i have decided to start adding Repositories/UnitOfWork/Service Layers etc.
So I have decided to create a Generic Repository to encapsulate general CRUD and record searching functionality.
"Stop babbling, what is your problem" i hear you say
I thought i would create a Get method which will take two Lambda parameters:
Here is the condensed Generic Repository code including the get method:
public class GenericRepository<TEntity> :
IRepository<TEntity> where TEntity : class
{
internal AccountsContext context;
internal DbSet<TEntity> dbSet;
/// <summary>
/// Default Constructor.
/// </summary>
/// <param name="context"></param>
public GenericRepository(AccountsContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter,
Expression<Func<TEntity, bool>> singleOrDefault)
{
IQueryable<TEntity> query = dbSet;
if (filter != null && singleOrDefault == null)
{
query = query.Where(filter);
}
if (filter != null && singleOrDefault != null)
{
query = query.Where(filter)
.SingleOrDefault(singleOrDefault);
}
return query.ToList();
}
Now the following compiler when i try and build this:
Error 1 Cannot implicitly convert type 'TEntity' to 'System.Linq.IQueryable'. An explicit conversion exists (are you missing a cast?) G:\Accountable\Accountable\DataAccessLayer\GenericRepository.cs 90 30 Accountable
The error lies on .SingleOrDefault(singleOrDefault) line.
For your convenience i have provided the extension method signatures im using:
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static TSource SingleOrDefault<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
I have been staring at this for ages trying to resolve it, any help would be greatly appreciated.
FYI
In my bloated code, i have the following type of code in my controllers which i want to replace with my repository methods, which is what im trying to replicate.
LedgerCustomer LedgerCustomer = db.LedgerCustomers.Where(l => l.RecordStatus != "X").SingleOrDefault(l => l.id == id);
CurrencyType currencyType = db.CurrencyTypes.Where(c => c.RecordStatus != "X")
.SingleOrDefault(c => c.id == LedgerCustomer.CurrencyTypeId);
Problem is:
A) Am i using the correct LINQ approach? The above queries from my bloated code are:
Select Ledger customer where the RecordStatus does not equal X (Logically deleted) and the id equals a particular id.
Because there should only be one record for LedgerCustomer PER id (as these are unique, I've used SingleOrDefault and Where to delselect any LedgerCustomer that is logically deleted.
B) Help with the compiler error would be helpful.
The problem lies (as you expected) on the line;
query = query.Where(filter).SingleOrDefault(singleOrDefault);
query
is an IQueryable, so using Where(filter).SingleOrDefault(singleOrDefault)
on it is quite ok.
The problem lies in that you're trying to assign the result of SingleOrDefault
back to query
.
SingleOrDefault
does not return an IQueryable, but instead just a single instance of TEntity
.