Search code examples
c#linqc#-4.0compiler-errorsenumerable

"An explicit conversion exists" - LINQ


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:

  1. filter = Select/Deselect records logic
  2. singleOrDefault = Provide one record or a default depending on 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.


Solution

  • 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.