none
Repository e Include Multiplo RRS feed

  • Domanda

  • Questo è il mio Generic Repository:

    public class GenericRepository<T> : IGenericRepository<T> where T : class
        {
            internal MyContext ctx;
            internal DbSet<T> dbSet;
    
            public GenericRepository(MyContext context)
            {
                this.ctx = context;
                this.dbSet = context.Set<T>();
                //disabilito il LazyLoading
                this.ctx.Configuration.LazyLoadingEnabled = false;
            }
    
            public IEnumerable<T> GetAll()
            {
                return dbSet.ToList();
            }
            public virtual T Find(object id)
            {
                return dbSet.Find(id);
            }
            public virtual T Start(Expression<Func<T, bool>> predicate)
            {
                return dbSet.FirstOrDefault(predicate);
            }
            public virtual IEnumerable<T> Where(Expression<Func<T, bool>> predicate)
            {
                return dbSet.Where(predicate);
            }

    Il mio problema è la parte inerente l'Include. Sebbene perfettamente funzionante, non è altrettanto elegante:

    public IEnumerable<T> Include(string text1)
            {
                return dbSet.Include(text1);
            }
            public IEnumerable<T> Include2(string text1, string text2)
            {
                return dbSet.Include(text1).Include(text2);
            }

    Infatti, mi piacerebbe un sistema che mi semplifichi il tutto, magari facendo ricorso alle espressioni come per il Where.

    Dando un occhiata in giro ho trovato qualcosa che dovrebbe essere la soluzione ideale, ma non riseco ad adattarlo al mio dbSet:

    public TEntity Item(Expression<Func<TEntity, bool>> wherePredicate, params Expression<Func<TEntity, object>>[] includeProperties)
        {
            foreach (var property in includeProperties)
            {
                _dbSet.Include(property);
            }
            return _dbSet.Where(wherePredicate).FirstOrDefault();
        }

    domenica 12 gennaio 2014 10:59

Risposte

  • Ho risolto così:

    public class GenericRepository<T> : IGenericRepository<T> where T : class
        {
            internal MyContext ctx;
            internal DbSet<T> dbSet;
    
            public GenericRepository(MyContext context)
            {
                this.ctx = context;
                this.dbSet = context.Set<T>();
                //disabilito il LazyLoading
                this.ctx.Configuration.LazyLoadingEnabled = false;
            }
            
            
            public IQueryable<T> FindBy(Expression<Func<T, bool>> wherePredicate, params Expression<Func<T, object>>[] includeProperties)
            {
                IQueryable<T> query = this.dbSet;
    
                foreach (var property in includeProperties)
                {
                    query = query.Include(property);
                }
    
                return query.Where(wherePredicate);
            }

    e la sua implementazione:

     entity = uow.Soggetti.FindBy(i => i.ID == key, n => n.Nascita, r => r.Residenza).SingleOrDefault();
    Comunque grazie per i suggerimeti. Chiudo
    • Contrassegnato come risposta piedatt80 lunedì 13 gennaio 2014 18:33
    lunedì 13 gennaio 2014 18:33

Tutte le risposte

  • Ciao,

    potresti usare un wrapper dell'ObjectQuery restituita dall'dbContext per ottenere qualcosa del genere (butto giù il codice senza averlo provato, potrebbe esserci qualche errore):

    public interface IRepositoryQuery<T> :IQueryable<T>
    {
      	IRepositoryQuery<T> Include(Func<T, string> propertyExpression);
    }
    
    internal class RepositoryQuery<T> : IRepositoryQuery<T>
    {
    
    	private ObjectQuery<T> query;
    
    	internal RepositoryQuery(ObjectQuery<T> query)
    	{
    		this.query = query;
    	}
    	public IRepositoryQuery<T> Include(Expression<Func<T,string>> property)
    	{
    		var propertyname = GetMemberName(property);
    		var query = new RepositoryQuery<T>(query.Include(propertyname));
    		return new query;
    	}
    
    	private static string GetMemberName(Expression method)
    	{
        	LambdaExpression lambda = method as LambdaExpression;
        	if (lambda == null)
            	throw new ArgumentNullException("method");
    
        	MemberExpression memberExpr = null;
    
        	if (lambda.Body.NodeType == ExpressionType.Convert)
        	{
            	memberExpr = 
                	((UnaryExpression)lambda.Body).Operand as MemberExpression;
        	}
        	else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
        	{
           	 memberExpr = lambda.Body as MemberExpression;		
    	}
    
        	if (memberExpr == null)
    	        throw new ArgumentException("method");
    
       	 return memberExpr.Member.Name;
    	}
    	//TODO: implement wrapped IQueryable methods...
    }


    L'implementazione nel repository del metodo Include sarà qualcosa di simile:

    public IRepositoryQuery<T> Include<T>(Expression<Func<T, string>> property)
            {
      var propertyname = GetMemberName(property);
    		var query = new RepositoryQuery<T>(dbset.Include(propertyname));
    		return query;
            }

    In questo modo puoi chiamare a cascata il metodo include:

    var query = repository.Include<Order>(o=>Product).Include(o=>o.Customer).Where(o=>Order.Product.Name == "Lumia 920");

    Gianluca


    Gianluca Carucci
    Software Engineer & Agile Coach
    ugidotnet|web|blog|@rucka


    domenica 12 gennaio 2014 11:58
  • Innanzitutto grazie per la risposta immediata.

    Ma sono alla ricerca di qualcosa di più semplice e/o più pratico....per i miei livelli.

    domenica 12 gennaio 2014 19:24
  • Ciao,

    potresti allora scegliere qualcosa di più grezzo e creare un extension method di IEnumerable o IQueryable, e richiamare il metodo include di ObjectQuery se questo è di tipo IEnumerable:

    internal static class IEnumerableExtension 
    {
      public static IEnumerable<T> Include<T>(this IEnumerable<T> source, Expression<Func<T, string>> property)
      {
        if (!(source is ObjectQuery<T>) 
        {
           return source;
        }
        var propertyname = GetMemberName(property); //l'implementazione del metodo è uguale a quello già fornito
        return (source as ObjectQuery<T>).Include;
      }
    }

    l'implementazione del metodo Include della classe GenericRepository sarebbe:

    public IEnumerable<T> Include<T>(Expression<Func<T, string>> property)
    {
      return this.dbSet<T>.Include(property);
    }

    e puoi richiamare a cascata il metodo Include del repository così come prima:

    var query = repository.Include<Order>(o=>Product).Include(o=>o.Customer).Where(o=>Order.Product.Name == "Lumia 920");

    Ricapitolando, molto meno codice, forse più semplice ma meno generico: supporta solo l'include di Entity Framework; non vedo una grossa limitazione visto che è rarissimo dover supportare più orm.

    Meglio?

    Gianluca

     

    Gianluca Carucci
    Software Engineer & Agile Coach
    ugidotnet|web|blog|@rucka

    domenica 12 gennaio 2014 19:59
  • Ho risolto così:

    public class GenericRepository<T> : IGenericRepository<T> where T : class
        {
            internal MyContext ctx;
            internal DbSet<T> dbSet;
    
            public GenericRepository(MyContext context)
            {
                this.ctx = context;
                this.dbSet = context.Set<T>();
                //disabilito il LazyLoading
                this.ctx.Configuration.LazyLoadingEnabled = false;
            }
            
            
            public IQueryable<T> FindBy(Expression<Func<T, bool>> wherePredicate, params Expression<Func<T, object>>[] includeProperties)
            {
                IQueryable<T> query = this.dbSet;
    
                foreach (var property in includeProperties)
                {
                    query = query.Include(property);
                }
    
                return query.Where(wherePredicate);
            }

    e la sua implementazione:

     entity = uow.Soggetti.FindBy(i => i.ID == key, n => n.Nascita, r => r.Residenza).SingleOrDefault();
    Comunque grazie per i suggerimeti. Chiudo
    • Contrassegnato come risposta piedatt80 lunedì 13 gennaio 2014 18:33
    lunedì 13 gennaio 2014 18:33