渴望加载一个字段 [英] Eager loading a field

查看:37
本文介绍了渴望加载一个字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以在使用 ContentManager 查询内容时预先加载字段?

我正在使用 ContentManager 检索特定内容类型的所有内容项.内容类型上有一个 MediaLibraryPickerField,当我遍历查询结果时,它会创建一个 select n+1 问题.我想强制预先加载这些数据(在初始查询时加入).这对于 ContentPart 来说似乎很简单,但我无法让它为 ContentField 工作.这是可能的还是有另一种方法可以避免字段的 select n+1 问题?

这是我尝试过但没有效果的方法:

var myQuery = _contentManager.Query(new[] { "MyContentType" }).WithQueryHints(new QueryHints().ExpandParts());

我也试过扩大记录:

var myQuery = _contentManager.Query(new[] { "MyContentType" }).WithQueryHints(new QueryHints().ExpandRecords());

解决方案

这是我修复投影页面问题的方法,但同样的方法,或者更简单的方法,也可以应用于您的情况.

在投影页面的 Content 形状的备用模板 Content-ProjectionPage.cshtml 中,我执行了以下操作,它为项目将以后可以使用:

//预取图片varprojectionItems = ((IEnumerable)((IEnumerable)Model.Content.Items).First(i => i.Metadata.Type == "List").Items).Select(s => (ContentItem)s.ContentItem);var mediaLibraryFields = 投影项.SelectMany(i => i.Parts.SelectMany(p => p.Fields.Where(f => f is MediaLibraryPickerField))).Cast<MediaLibraryPickerField>();var firstMediaIds = mediaLibraryFields.Select(f => f.Ids.FirstOrDefault()).Where(id => id != default(int)).清楚的().ToArray();var firstMedia = WorkContext.Resolve().GetMany<MediaPart>(firstMediaIds, VersionOptions.Published, QueryHints.Empty);var mediaCache = Layout.MediaCache == null?Layout.MediaCache = new Dictionary(): (Dictionary) Layout.MediaCache;foreach (var media in firstMedia) {mediaCache.Add(media.Id, media);}

在您的情况下,您不必对形状进行复杂的钻探来挖掘字段,因为您可以直接访问它们.我不得不这样做,因为不幸的是,视图或形状表提供程序是我最容易做到的地方.

然后,当我想要显示图像时,我所要做的就是访问我的查找并尝试从那里获取它.在我的备用模板 MediaLibraryPicker.Summary.cshtml 中,我这样做:

var field = (MediaLibraryPickerField)Model.ContentField;var imageIds = field.Ids;如果(imageIds.Any()){var cm = Model.ContentPart.ContentItem.ContentManager 作为 IContentManager;var title = cm == null ||Model.ContentPart == null?"" : cm.GetItemMetadata(Model.ContentPart).DisplayText;var mediaCache = Layout.MediaCache as Dictionary;var firstImage = mediaCache != null?mediaCache[imageIds.First()]: cm.Get(imageIds.First()).As();<div class="gallery"><a href="@Url.ItemDisplayUrl((IContent)Model.ContentPart)"><img src="@Display.ResizeMediaUrl(Path: firstImage.MediaUrl, Width: 132)" class="main" alt="@title"/></a>

}

我只在此处显示字段中的第一张图像,但您可以在 f.Ids.FirstOrDefault() 的位置更改该图像.只需执行 f.Ids 并将 Select 替换为 SelectMany.还要更改摘要模板,以便在同一字典中查找所有图像后显示所有图像.

一旦我这样做了,我就没有选择 N+1,而是对页面上的所有图像进行了单个 SQL 查询.

Is it possible to eager load a field when querying content using the ContentManager?

I'm using the ContentManager to retrieve all content items of a specific content type. The content type has a MediaLibraryPickerField on it which is creating a select n+1 issue when I iterate over the results of the query. I'd like to force this data to be loaded upfront (join on initial query). This seems straightforward for a ContentPart but I can't get it to work for a ContentField. Is this possible or is there another way to avoid the select n+1 issue with fields?

Here's what I've tried but it has not effect:

var myQuery = _contentManager.Query(new[] { "MyContentType" })
        .WithQueryHints(new QueryHints().ExpandParts<MediaPart>());

I've also tried expanding the record:

var myQuery = _contentManager.Query(new[] { "MyContentType" })
        .WithQueryHints(new QueryHints().ExpandRecords<MediaPartRecord>());

解决方案

Here's how I fixed the problem for a projection page, but the same method, or something simpler, could be applied in your case.

In an alternate template for the Content shape of the projection page, Content-ProjectionPage.cshtml, I did the following, which creates a lookup for media that items will be able to use later:

// Pre-fetch images
var projectionItems =  ((IEnumerable<dynamic>)
    ((IEnumerable<dynamic>)Model.Content.Items)
    .First(i => i.Metadata.Type == "List").Items)
    .Select(s => (ContentItem)s.ContentItem);
var mediaLibraryFields = projectionItems
    .SelectMany(i => i.Parts.SelectMany(p => p.Fields.Where(f => f is MediaLibraryPickerField)))
    .Cast<MediaLibraryPickerField>();
var firstMediaIds = mediaLibraryFields
    .Select(f => f.Ids.FirstOrDefault())
    .Where(id => id != default(int))
    .Distinct()
    .ToArray();
var firstMedia = WorkContext.Resolve<IContentManager>()
    .GetMany<MediaPart>(firstMediaIds, VersionOptions.Published, QueryHints.Empty);
var mediaCache = Layout.MediaCache == null 
    ? Layout.MediaCache = new Dictionary<int, MediaPart>() 
    : (Dictionary<int, MediaPart>) Layout.MediaCache;
foreach (var media in firstMedia) {
    mediaCache.Add(media.Id, media);
}

In your case, you don't have to do the complicated drilling into shapes to dig out the fields, as you have access to them directly. I had to do that because the view or a shape table provider is unfortunately the easiest place for me to do that.

Then, when I want to display an image, all I have to do is access my lookup and try to get it from there. In my alternate template MediaLibraryPicker.Summary.cshtml, I do this:

var field = (MediaLibraryPickerField)Model.ContentField;
var imageIds = field.Ids;
if (imageIds.Any()) {
    var cm = Model.ContentPart.ContentItem.ContentManager as IContentManager;
    var title = cm == null || Model.ContentPart == null
        ? "" : cm.GetItemMetadata(Model.ContentPart).DisplayText;

    var mediaCache = Layout.MediaCache as Dictionary<int, MediaPart>;
    var firstImage = mediaCache != null
        ? mediaCache[imageIds.First()]
        : cm.Get(imageIds.First()).As<MediaPart>();
    <div class="gallery">
        <a href="@Url.ItemDisplayUrl((IContent)Model.ContentPart)"><img src="@Display.ResizeMediaUrl(Path: firstImage.MediaUrl, Width: 132)" class="main" alt="@title"/></a>
    </div>
}

I'm only displaying the first image in the field, here, but you could change that where it does f.Ids.FirstOrDefault(). Just do f.Ids instead and replace the Select with a SelectMany. Also change the summary template so it displays all images after looking them up in the same dictionary.

Once I did that, I had no select N+1, and instead got a single SQL query for all the images on the page.

这篇关于渴望加载一个字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆