77 votes

EF incluant d'autres entités (modèle de référentiel générique)

J'utilise le modèle Generic Repository au-dessus d'Entity Framework Code First. Tout fonctionnait bien jusqu'à ce que je doive inclure plusieurs entités dans une requête. J'ai réussi à inclure une entité avec succès, mais maintenant je n'arrive pas à comprendre comment inclure plusieurs entités. Regardez ce que j'ai obtenu jusqu'à présent :

public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
    var entityName = GetEntityName<TEntity>();
    return _objectContext.CreateQuery<TEntity>(entityName);
}

public IList<TEntity> GetQueryWithInclude<TEntity>(string toInclude) where TEntity : class
{
    var entityName = GetEntityName<TEntity>();
    return _objectContext.CreateQuery<TEntity>(entityName).Include(toInclude).ToList();
}

private string GetEntityName<TEntity>() where TEntity : class
{
    return string.Format("{0}.{1}", _objectContext.DefaultContainerName, _pluralizer.Pluralize(typeof(TEntity).Name));
}

Ce que j'ai essayé de faire, mais qui n'a pas fonctionné, c'est de passer un tableau de chaînes dans une fonction, puis d'essayer d'"ajouter" les inclusions en haut de la requête. Je me demandais si je pouvais appeler la fonction GetQueryWithInclude et passer un nom d'entité (en fait une propriété de navigation) à la fois pour agréger les résultats de la requête, mais je crains que cela ne duplique les résultats de la requête à chaque appel... À votre avis, quelle serait la meilleure façon de faire fonctionner cette méthode ?

Merci d'avance !

UPDATE :

Voici un exemple de ce que j'essaie d'obtenir :

public IQueryable GetQueryWithIncludes(string[] otherEntities)
{
    var entityName = GetEntityName<TEntity>();
    //now loop over the otherEntities array 
    //and append Include extensions to the query
    //so inside the loop, something like: 
    _objectContext.GetQuery<TEntity>(entityName).Include(otherEntities[index]);
}

0 votes

Donner des précisions sur "inclure plus d'entités dans une requête" ? Pouvez-vous en donner un exemple ? Si vous avez un ObjectContext vous devriez pouvoir interroger un objet ou des objets connexes avec LinqToEntities.

0 votes

@giddy : Regardez la mise à jour ci-dessus. Merci.

147voto

Ladislav Mrnka Points 218632

Utilisez uniquement l'extension Include sur IQueryable. Elle est disponible dans l'assembly EF 4.1. Si vous ne voulez pas référencer cet assembly dans vos couches supérieures, créez une méthode d'extension wrapper dans votre assembly d'accès aux données.

Ici, vous avez un exemple :

public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params Expression<Func<T, object>>[] includes)
    where T : class
{
    if (includes != null)
    {
        query = includes.Aggregate(query, 
                  (current, include) => current.Include(include));
    }

    return query;
}

Vous l'utiliserez par exemple pour :

var query = context.Customers
                   .IncludeMultiple(
                       c => c.Address,
                       c => c.Orders.Select(o => o.OrderItems));

Cette requête chargera tous les clients avec leurs adresses et leurs commandes et chaque commande contiendra ses éléments de commande.

0 votes

@Ladislav Mrnka : C'est exactement ce que j'essaie de faire. J'essaie d'utiliser l'extension Include, mais je veux créer une méthode générique qui accepte un nombre quelconque de propriétés de navigation dans un tableau de chaînes de caractères, puis les inclure dans l'entité interrogée. Voir ma modification/mise à jour ci-dessus.

3 votes

N'utilisez pas de version avec des cordes. EF 4.1 propose également une version fortement typée avec des lambdas.

0 votes

@Ladislav Mrnka : Ok mais comment ? Pouvez-vous nous donner un exemple ?

3voto

Shimmy Points 23393

Dites adieu aux appels codés en dur à ObjectQuery(T).Include.

Si vous utilisez EF > 4, c'est intégré, vérifiez les points suivants DbExtensions.Include sur MSDN .

3voto

jvelez Points 641

//J'ai inclus le strict minimum ici. Vous trouverez ci-dessous comment l'utiliser.

     IQueryable<File> xg= UnitOfWork.Files.GetAllLazyLoad(d => d.FileId == 1, 
            r => r.FileCategory);
//where r.FileCategory is a navigational property.

//Interface

    namespace Msh.Intranet.Repository.GenericRepoPattern
    {
        public interface IRepository<T> where T:class
        {

            IQueryable<T> GetAllLazyLoad(Expression<Func<T, bool>> filter, params Expression<Func<T, object>>[] children);

        }
    }

        namespace Msh.Intranet.Repository.GenericRepoPattern
        {
            /// <summary>
            /// The EF-dependent, generic repository for data access
            /// </summary>
            /// <typeparam name="T">Type of entity for this Repository.</typeparam>
            public class EFRepository<T> : IRepository<T> where T : class
            {
                public EFRepository(DbContext dbContext)
                {
                    if (dbContext == null)
                        throw new ArgumentNullException("dbContext");
                    DbContext = dbContext;
                    DbSet = DbContext.Set<T>();

                }

                protected DbContext DbContext { get; set; }

                protected DbSet<T> DbSet { get; set; }

                public virtual IQueryable<T> GetAllLazyLoad(Expression<Func<T, bool>> filter, params Expression<Func<T, object>>[] children) 
                {

                        children.ToList().ForEach(x=>DbSet.Include(x).Load());
                        return DbSet;
                }

            }
        }

1 votes

Il semble que la méthode GetAllLazyLoad de votre exemple charge en fait les données de manière accélérée. Si j'ai bien compris, le chargement paresseux se fait sans l'include, en extrayant les données lors du premier accès. Ce que votre méthode semble montrer, c'est l'utilisation de l'include qui extrait les données lors de la création de l'objet.

2voto

Bibin Gangadharan Points 469

Pour le cadre de l'entité, créez une liste de chaînes de caractères pour contenir les inclusions. Voir l'exemple de code ci-dessous

public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> filter,
                                              int skip = 0,
                                              int take = int.MaxValue,
                                              Func<IQueryable<TObject>, IOrderedQueryable<TObject>> orderBy = null,
                                              IList<string> incudes = null)
    {
        var _resetSet = filter != null ? DbSet.AsNoTracking().Where(filter).AsQueryable() : DbSet.AsNoTracking().AsQueryable();

        if (incudes != null)
        {
            foreach (var incude in incudes)
            {
                _resetSet = _resetSet.Include(incude);
            }
        }
        if (orderBy != null)
        {
            _resetSet = orderBy(_resetSet).AsQueryable();
        }
        _resetSet = skip == 0 ? _resetSet.Take(take) : _resetSet.Skip(skip).Take(take);

        return _resetSet.AsQueryable();
    }

Pour plus de détails, voir le lien ci-dessous http://bulletproofcoder.com/blog/using-include-in-a-generic-entity-framework-repository

Sur cadre de base de l'entité nous utiliserons IIncludableQueryable . Voir l'exemple de code ci-dessous

        public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> filter,
                                              int skip = 0,
                                              int take = int.MaxValue,
                                              Func<IQueryable<TObject>, IOrderedQueryable<TObject>> orderBy = null,
                                              Func<IQueryable<TObject>, IIncludableQueryable<TObject, object>> include = null)
    {
        var _resetSet = filter != null ? DbSet.AsNoTracking().Where(filter).AsQueryable() : DbSet.AsNoTracking().AsQueryable();

        if (include != null)
        {
            _resetSet = include(_resetSet);
        }
        if (orderBy != null)
        {
            _resetSet = orderBy(_resetSet).AsQueryable();
        }
        _resetSet = skip == 0 ? _resetSet.Take(take) : _resetSet.Skip(skip).Take(take);

        return _resetSet.AsQueryable();
    }

1 votes

Pourriez-vous nous en dire un peu plus ? S'agit-il d'une méthode Filter écrite par vous-même et utilisant IIncludableQueryable ? Et comment l'utilisez-vous dans le code ?

1 votes

Oui, c'est la méthode du filtre à retour automatique. IIncludableQueryable est une interface fournie par le code EF. Plus de détails : docs.microsoft.com/fr/us/dotnet/api/ Exemple de code (j'ai écrit pour mon projet) : var clientStores = await _unitOfWork.ClientStoreRepository.Filter(predicate, skip : skip, take : take, orderBy : c => c.OrderBy(a => a.StoreName), include : s => s.Include(l => l.Location).ThenInclude(la => la.LocationArea) .Include(c => c.Country))).ToListAsync() ;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X