none
Repository, Include() and Find() RRS feed

  • Question

  • Hi

    I am creating a Repository, this is a little section of code:

    public class RepositoryBase<T> : IRepository<T> where T : class
        {
    
            
            public virtual T GetById(int id, params Expression<Func<T, object>>[] includes)
            {
                List<string> includelist = new List<string>();
    
                foreach (var item in includes)
                {
                    MemberExpression body = item.Body as MemberExpression;
                    if (body == null)
                        throw new ArgumentException("The body must be a member expression");
    
                    includelist.Add(body.Member.Name);
                }
    
                using (NombreDbContext context = new NombreDbContext())
                {
                    //DbQuery<T> entity = context.Set<T>();
                    DbSet<T> entity = context.Set<T>();
    
                    includelist.ForEach(x => entity = entity.Include(x));
                    
                    return entity.Find(id);  
    
                }
    
            }
    
    }

    I am having a problem in the line

    includelist.ForEach(x => entity = entity.Include(x));

    the error

    Cannot implicitly convert type 'System.Data.Entity.Infrastructure.DbQuery<T>' to 'System.Data.Entity.DbSet<T>'. An explicit conversion exists (are you missing a cast?)  

    How I can use Include() with the Find() ? the problem is the conversion between DbQuery and DbSet

    the idea is to use the Find(), if this method can not be used will be necessary to determine the entity's key

    regards


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina

    Tuesday, September 25, 2012 5:22 PM

Answers

  • You'd probably need to pass in an Expression<Func<T, bool>> key selector to the method, so you could call it with the appropriate key based on the entity type.

    This would look something like:

    public virtual T GetById(Expression<Func<T, bool>> keySelector, params Expression<Func<T, object>>[] includes)
            {
                List<string> includelist = new List<string>();
    
                foreach (var item in includes)
                {
                    MemberExpression body = item.Body as MemberExpression;
                    if (body == null)
                        throw new ArgumentException("The body must be a member expression");
    
                    includelist.Add(body.Member.Name);
                }
    
                using (NombreDbContext context = new NombreDbContext())
                {
                    DbQuery<T> entity = context.Set<T>();
    
                    includelist.ForEach(x => entity = entity.Include(x));
                    
                    return entity.First(keySelector);  
                }
            }


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Tuesday, September 25, 2012 7:28 PM
  • Finally the solution


    public abstract class RepositoryBase<T> : IRepository<T> where T : class { public abstract T GetById(int id, params Expression<Func<T, object>>[] includes); protected virtual T GetById(Expression<Func<T, bool>> keySelector, params Expression<Func<T, object>>[] includes) { List<string> includelist = new List<string>(); foreach (var item in includes) { MemberExpression body = item.Body as MemberExpression; if (body == null) throw new ArgumentException("The body must be a member expression"); includelist.Add(body.Member.Name); } using (NameDbContext context = new NameDbContext()) { DbQuery<T> query = context.Set<T>(); includelist.ForEach(x => query = query.Include(x)); return query.FirstOrDefault(keySelector); } }

    }

    and the concret repository

    public class UserRepository : RepositoryBase<User>, IUserRepository
    {
    
            public override User GetById(int id, params Expression<Func<User, object>>[] includes)
            {
                return base.GetById(x=>x.UserId == id, includes);
            }
    
    }

    with the "abstract" I make sure that the lambda is defined for the filter that requires GetById()

    regards


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina


    Tuesday, September 25, 2012 8:21 PM

All replies

  • You need the cast:

     includelist.ForEach(x => entity = (DbSet<T>)entity.Include(x));
    

    When you call .Include(), the result is a DbQuery<T>, not the original type [DbSet<T>].  However, there is an explicit conversion, so the cast should work.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Tuesday, September 25, 2012 5:29 PM
  • Hi

    thanks for your answer

    I use this cast, but I get the error:

    Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery`1[NombreDbContext.Entities.User]' to type 'System.Data.Entity.DbSet`1[NombreDbContext.Entities.User]'.

    and highlight:

    includelist.ForEach(x => entity = (DbSet<T>)entity.Include(x));

    regards


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina


    Tuesday, September 25, 2012 6:15 PM
  • You have to make entity a DbQuery<T>:

     using (NombreDbContext context = new NombreDbContext())
                {
                    DbSet<T> entity = context.Set<T>();
                    DbQuery<T> query = entity;
    
                    includelist.ForEach(x => query = query.Include(x));
                    
                    return query.Find(id);  
    
                }


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Tuesday, September 25, 2012 6:35 PM
  • But query is DbQuery and it haven't the Find() method to search by entity key

    the line

      return query.Find(id); 

    is marked with a compilation error

    System.Data.Entity.Infrastructure.DbQuery<T>' does not contain a definition for 'Find' and no extension method 'Find' accepting a first argument of type 'System.Data.Entity.Infrastructure.DbQuery<T>' could be found (are you missing a using directive or an assembly reference?)  

    Find() is defined in the DbSet<>

    -----------

    I change the line

    return entity.Find(id);

    but in this case el Include() doesn't work, then related properties isn't load


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina

    Tuesday, September 25, 2012 6:51 PM
  • Arg - I'm sorry.

    Yeah, unfortunately, right now, you have to just query the ID directly.  Include() doesnt' work with Find().  See: http://social.msdn.microsoft.com/Forums/zh/adonetefx/thread/80dd081b-2971-4632-bf70-72a80f550f86


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Tuesday, September 25, 2012 6:56 PM
  • then, the problem is: how I can know the entity's key?

    The solution would be to define:

         query.First(x=> x.Id == value)

    but the id property is different with each entity, how I can define it dinamically


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina


    Tuesday, September 25, 2012 7:21 PM
  • You'd probably need to pass in an Expression<Func<T, bool>> key selector to the method, so you could call it with the appropriate key based on the entity type.

    This would look something like:

    public virtual T GetById(Expression<Func<T, bool>> keySelector, params Expression<Func<T, object>>[] includes)
            {
                List<string> includelist = new List<string>();
    
                foreach (var item in includes)
                {
                    MemberExpression body = item.Body as MemberExpression;
                    if (body == null)
                        throw new ArgumentException("The body must be a member expression");
    
                    includelist.Add(body.Member.Name);
                }
    
                using (NombreDbContext context = new NombreDbContext())
                {
                    DbQuery<T> entity = context.Set<T>();
    
                    includelist.ForEach(x => entity = entity.Include(x));
                    
                    return entity.First(keySelector);  
                }
            }


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Tuesday, September 25, 2012 7:28 PM
  • I was just thinking the same solution

    I perform some tests and come back with the result


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina

    Tuesday, September 25, 2012 7:44 PM
  • Finally the solution


    public abstract class RepositoryBase<T> : IRepository<T> where T : class { public abstract T GetById(int id, params Expression<Func<T, object>>[] includes); protected virtual T GetById(Expression<Func<T, bool>> keySelector, params Expression<Func<T, object>>[] includes) { List<string> includelist = new List<string>(); foreach (var item in includes) { MemberExpression body = item.Body as MemberExpression; if (body == null) throw new ArgumentException("The body must be a member expression"); includelist.Add(body.Member.Name); } using (NameDbContext context = new NameDbContext()) { DbQuery<T> query = context.Set<T>(); includelist.ForEach(x => query = query.Include(x)); return query.FirstOrDefault(keySelector); } }

    }

    and the concret repository

    public class UserRepository : RepositoryBase<User>, IUserRepository
    {
    
            public override User GetById(int id, params Expression<Func<User, object>>[] includes)
            {
                return base.GetById(x=>x.UserId == id, includes);
            }
    
    }

    with the "abstract" I make sure that the lambda is defined for the filter that requires GetById()

    regards


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina


    Tuesday, September 25, 2012 8:21 PM