C # Entity-Framework: Как я могу объединить .Find и .Include для модельного объекта? - PullRequest
130 голосов
/ 08 сентября 2011

Я занимаюсь практическим руководством по mvcmusicstore.Я заметил кое-что при создании скаффолда для менеджера альбомов (добавить, удалить, изменить).

Я хочу элегантно написать код, поэтому я ищу чистый способ написать это.

К вашему сведениюЯ делаю магазин более общим:

Альбомы = Предметы

Жанры = Категории

Исполнитель = Бренд

Вот как получается индекс (генерируется MVC):

var items = db.Items.Include(i => i.Category).Include(i => i.Brand);

Вот как извлекается элемент для удаления:

Item item = db.Items.Find(id);

Первый возвращает все элементы и заполняет категорию и модели бренда внутримодель товара.Второй, не заполняет категорию и бренд.

Как я могу написать второй, чтобы сделать поиск И заполнить что внутри (желательно в 1 строку) ... теоретически - что-то вроде:

Item item = db.Items.Find(id).Include(i => i.Category).Include(i => i.Brand);

Ответы [ 5 ]

139 голосов
/ 08 сентября 2011

Сначала необходимо использовать Include(), а затем извлечь отдельный объект из полученного запроса:

Item item = db.Items
              .Include(i => i.Category)
              .Include(i => i.Brand)
              .SingleOrDefault(x => x.ItemId == id);
60 голосов
/ 21 марта 2014

В ответе Денниса используются Include и SingleOrDefault.Последний выполняет циклическое переключение в базу данных.

Альтернативой является использование Find в сочетании с Load для явной загрузки связанных объектов ...

Ниже пример 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(); 
}

Конечно, Find немедленно возвращается без отправки запроса в хранилище, если этот объект уже загружен контекстом.

0 голосов
/ 12 июня 2019

Вы должны привести IQueryable к DbSet

var dbSet = (DbSet<Item>) db.Set<Item>().Include("");

return dbSet.Find(id);

0 голосов
/ 21 марта 2019

Нет простого способа фильтрации с помощью поиска.Но я придумал близкий способ репликации функциональности, но, пожалуйста, обратите внимание на несколько вещей для моего решения.

Это решение позволяет вам выполнять общую фильтрацию, не зная первичного ключа в .net-core

  1. Поиск принципиально отличается, поскольку он получает объект, если он присутствует в отслеживании, перед запросом базы данных.

  2. Кроме того, он может фильтровать по объекту, поэтому пользователю не нужно знать первичный ключ.

  3. Это решение предназначено для EntityFramework Core.

  4. Для этого требуется доступ к контексту

Вот несколько добавленных методов расширения, которые помогут вам выполнить фильтрацию по первичному ключу, поэтому

    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));
    }

Один разу вас есть эти методы расширения, которые вы можете отфильтровать так:

query.FilterByPrimaryKey(this._context, id);
0 голосов
/ 10 сентября 2011

не работает для меня.Но я решил это следующим образом.

var item = db.Items
             .Include(i => i.Category)
             .Include(i => i.Brand)
             .Where(x => x.ItemId == id)
             .First();

Не знаю, будет ли это правильным решением.Но другой Деннис дал мне ошибку bool в .SingleOrDefault(x => x.ItemId = id);

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...