3

Как известно в EF-Core нет никакой ленивой загрузки. Таким образом, это означает, что я вынужден делать свои запросы с некоторой запоздалой мыслью. Поэтому, поскольку я должен думать, тогда я мог бы попытаться сделать это правильно.Условное включение и последующее включение

У меня есть стандартный запрос на обновление, но я думал, что мне не всегда нужно включать 0Kи PromoImage FK-объекты. Должен быть способ сделать так. Но я могу просто не найти способ выполнить Include в более поздней точке. На самом деле я хотел бы, возможно, включить прямо перед тем, как я действительно буду работать над объектом. Таким образом, я мог бы автоматизировать некоторые из них.

ArticleContent ac = _ctx.ArticleContents 
    .Include(a=> a.Metadata) 
    .Include(a=> a.HeaderImage) 
    .Include(a=> a.PromoImage) 
    .Single(a => a.Id == model.BaseID); 

ac.Title = model.Title; 
ac.Ingress = model.Ingress; 
ac.Body = model.Body; 
ac.Footer = model.Footer; 

if (model.HeaderImage != null) 
{ 
    ac.HeaderImage.FileURL = await StoreImage(model.HeaderImage, $"Header_{model.Title.Replace(" ", "_")}_{rand.Next()}"); 
} 
if (model.PromoImage != null) 
{ 
    ac.PromoImage.FileURL = await StoreImage(model.PromoImage, $"Promo_{model.Title.Replace(" ", "_")}_{rand.Next()}"); 
} 

ac.Metadata.EditedById = uId; 
ac.Metadata.LastChangedTimestamp = DateTime.Now; 

await _ctx.SaveChangesAsync(); 

EXTRA

Чтобы было ясно, что это EF7 (ядро), и им после того, как решение, которое позволяет мне добавить включает по запросу, надеюсь, после первоначального _ctx.ArticleContents.Include(a=> a.Metadata).Single(a => a.Id == model.BaseID).

+0

Я не думаю, что вы собираетесь быть в состоянии сделать это после того как вы» ve called Single - это материализует объект из базы данных в память, принимая во внимание все Включенные, которые вы указали до этой точки. Чтобы иметь возможность сделать это после того, как факт будет состоять в том, чтобы попросить фреймворк получить дополнительные данные поверх того, что он уже прочитал (т. Е. Вы пытаетесь заставить ленивую загрузку загружать в режим ожидания) –

+0

@MikeGoatly Да, я «Боюсь, что лучший ответ, мое решение мечты будет гибридом между нетерпеливой и ленивой загрузкой. должно существовать что-то вроде 'IncludeIf (a => a.PromoImage, true | false)', что было бы действительно оптимальным решением для моей мечты –

ответ

0

Я бы создал метод для извлечения данных, которые принимают свойства для включения в виде выражений:

static ArticleContent GetArticleContent(int ID, 
    params Expression<Func<ArticleContent, object>>[] includes) 
{ 
    using(var ctx = new MyContext()) 
    { 
     var acQuery = _ctx.ArticleContents.Include(a=> a.Metadata); 
     foreach(var include in includes) 
     acQuery = acQuery.Include(include); 
     return acQuery.Single(a => a.Id == model.BaseID); 
    } 
} 

А потом в основном методе:

var ac = GetArticleContent(3, a => a.HeaderImage, a => a.PromoImage); 
+0

Хм, это довольно близко к тому, что я надеялся, что он все еще немного «заперт» ", но он ломается на' acQuery.Include (include) 'Ошибка:« Невозможно неявно преобразовать тип »Microsoft.Data.Entity.Query.IIncludableQueryable 'to' Microsoft.Data. Entity.Query.IIncludableQueryable ''пробовал некоторые« обходные пути », но безрезультатно. –

+0

Удаление GeneralMetadata дает просто «Невозможно неявно преобразовать тип» Microsoft.Data.Entity.Query.IIncludableQueryable 'to' Microsoft.Data.Entity.DbSet '' –

+0

@ ThomasAndreèLian Просто попробовал в моем собственном db, и он работает, вы уверены, что у вас есть« System.Data.Entity »? Кроме того, я получаю сообщение об ошибке без первого включения (не в foreach), я могу разрешить его, выполнив «var acQuery = ctx.ArticleContents.Select (a => a);' –

1

Я использую нечто похожее на решение Александра Derck в. (Относительно исключения, упомянутого в комментариях: ctx.ArticleContents.AsQueryable() также должен работать.)

Для нескольких сайтов CRUD MVC я использую BaseAdminController. В производных конкретных контроллерах я могу добавить Включает динамически. Из BaseAdminController:

// TModel: e.g. ArticleContent 
private List<Expression<Func<TModel, object>>> includeIndexExpressionList = new List<Expression<Func<TModel, object>>>(); 

protected void AddIncludes(Expression<Func<TModel, object>> includeExpression) 
{ 
    includeIndexExpressionList.Add(includeExpression); 
} 

Позже я понял, что мне нужно больше гибкости, так что я добавил Queryable. Например. для ThenInclude().

private Func<IQueryable<TModel>, IQueryable<TModel>> IndexAdditionalQuery { get; set; } 

protected void SetAdditionalQuery(Func<IQueryable<TModel>, IQueryable<TModel>> queryable) 
{ 
    IndexAdditionalQuery = queryable; 
} 

Здесь индекс действия:

public virtual async Task<IActionResult> Index() 
{ 
    // dynamic include: 
    // dbset is for instance ctx.ArticleContents 
    var queryable = includeIndexExpressionList 
     .Aggregate(dbSet.AsQueryable(), (current, include) => current.Include(include)); 
    if(IndexAdditionalQuery != null) queryable = IndexAdditionalQuery(queryable); 
    var list = await queryable.Take(100).AsNoTracking().ToListAsync(); 
    var viewModelList = list.Map<IList<TModel>, IList<TViewModel>>(); 

    return View(viewModelList); 
} 

В конкретном контроллере я использую:

AddIncludes(e => e.EventCategory); 

SetAdditionalQuery(q => q 
    .Include(e => e.Event2Locations) 
    .ThenInclude(j => j.Location)); 
Смежные вопросы