Je fais le tutoriel pratique mvcmusicstore. J'ai remarqué quelque chose lors de la création de l'échafaudage du gestionnaire d'albums (ajout, suppression, édition).
Je veux écrire du code avec élégance, alors je cherche la façon propre d’écrire cela.
Pour info, je rends le magasin plus générique:
Albums = Articles
Genres = Catégories
Artiste = Marque
Voici comment récupérer l'index (généré par MVC):
var items = db.Items.Include(i => i.Category).Include(i => i.Brand);
Voici comment l’élément à supprimer est récupéré:
Item item = db.Items.Find(id);
Le premier ramène tous les articles et renseigne les modèles de catégorie et de marque à l'intérieur du modèle d'article. La seconde ne renseigne pas la catégorie et la marque.
Comment puis-je écrire le deuxième pour faire la recherche ET peupler ce qui est à l'intérieur (de préférence en 1 ligne) ... en théorie - quelque chose comme:
Item item = db.Items.Find(id).Include(i => i.Category).Include(i => i.Brand);
Vous devez d'abord utiliser Include()
, puis récupérer un seul objet à partir de la requête résultante:
Item item = db.Items
.Include(i => i.Category)
.Include(i => i.Brand)
.SingleOrDefault(x => x.ItemId == id);
La réponse de Dennis utilise Include
et SingleOrDefault
. Ce dernier fait le tour de la base de données.
Une alternative consiste à utiliser Find
, en combinaison avec Load
, pour le chargement explicite d'entités associées ...
Ci-dessous exemple MSDN :
using (var context = new BloggingContext())
{
var post = context.Posts.Find(2);
// Load the blog related to a given post
context.Entry(post).Reference(p => p.Blog).Load();
// Load the blog related to a given post using a string
context.Entry(post).Reference("Blog").Load();
var blog = context.Blogs.Find(1);
// Load the posts related to a given blog
context.Entry(blog).Collection(p => p.Posts).Load();
// Load the posts related to a given blog
// using a string to specify the relationship
context.Entry(blog).Collection("Posts").Load();
}
Bien entendu, Find
retourne immédiatement sans faire de demande au magasin, si cette entité est déjà chargée par le contexte.
Cela n'a pas fonctionné pour moi. Mais je l'ai résolu en faisant comme ça.
var item = db.Items
.Include(i => i.Category)
.Include(i => i.Brand)
.Where(x => x.ItemId == id)
.First();
Je ne sais pas si c'est une solution acceptable. Mais l'autre que Dennis m'a donné m'a donné une erreur bool dans .SingleOrDefault(x => x.ItemId = id);
Vous devez lancer IQueryable sur DbSet
var dbSet = (DbSet<Item>) db.Set<Item>().Include("");
return dbSet.Find(id);
Il n'y a pas vraiment de moyen facile de filtrer avec une recherche. Mais je suis venu avec un moyen étroit de reproduire la fonctionnalité, mais s'il vous plaît prendre note de quelques choses pour ma solution.
Cette solution vous permet de filtrer de manière générique sans connaître la clé primaire dans .net-core
Find est fondamentalement différent car il obtient l'entité si elle est présente dans le suivi avant d'interroger la base de données.
De plus, il peut filtrer par objet, évitant ainsi à l'utilisateur de connaître la clé primaire.
Cette solution est destinée à EntityFramework Core.
Voici quelques méthodes d'extension à ajouter qui vous aideront à filtrer par clé primaire afin
public static IReadOnlyList<IProperty> GetPrimaryKeyProperties<T>(this DbContext dbContext)
{
return dbContext.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties;
}
//TODO Precompile expression so this doesn't happen everytime
public static Expression<Func<T, bool>> FilterByPrimaryKeyPredicate<T>(this DbContext dbContext, object[] id)
{
var keyProperties = dbContext.GetPrimaryKeyProperties<T>();
var parameter = Expression.Parameter(typeof(T), "e");
var body = keyProperties
// e => e.PK[i] == id[i]
.Select((p, i) => Expression.Equal(
Expression.Property(parameter, p.Name),
Expression.Convert(
Expression.PropertyOrField(Expression.Constant(new { id = id[i] }), "id"),
p.ClrType)))
.Aggregate(Expression.AndAlso);
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
public static Expression<Func<T, object[]>> GetPrimaryKeyExpression<T>(this DbContext context)
{
var keyProperties = context.GetPrimaryKeyProperties<T>();
var parameter = Expression.Parameter(typeof(T), "e");
var keyPropertyAccessExpression = keyProperties.Select((p, i) => Expression.Convert(Expression.Property(parameter, p.Name), typeof(object))).ToArray();
var selectPrimaryKeyExpressionBody = Expression.NewArrayInit(typeof(object), keyPropertyAccessExpression);
return Expression.Lambda<Func<T, object[]>>(selectPrimaryKeyExpressionBody, parameter);
}
public static IQueryable<TEntity> FilterByPrimaryKey<TEntity>(this DbSet<TEntity> dbSet, DbContext context, object[] id)
where TEntity : class
{
return FilterByPrimaryKey(dbSet.AsQueryable(), context, id);
}
public static IQueryable<TEntity> FilterByPrimaryKey<TEntity>(this IQueryable<TEntity> queryable, DbContext context, object[] id)
where TEntity : class
{
return queryable.Where(context.FilterByPrimaryKeyPredicate<TEntity>(id));
}
Une fois que vous avez ces méthodes d'extension, vous pouvez filtrer comme suit:
query.FilterByPrimaryKey(this._context, id);