-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RenderForm causes SQL queries because it uses the IContentService #400
Comments
Thanks, this is going on the list of things to consider when we are picking up new issues to improve performance. 👍 |
Using the The fix is quite easy (thankfully the using System.Collections;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Forms.Core.Components;
using Umbraco.Forms.Core.Services;
using Umbraco.Web;
public class PublishedContentPageService : IPageService
{
protected readonly IUmbracoContextAccessor umbracoContextAccessor;
public PublishedContentPageService(IUmbracoContextAccessor umbracoContextAccessor)
{
this.umbracoContextAccessor = umbracoContextAccessor;
}
public Hashtable GetPageElements() => this.GetPageElements(this.umbracoContextAccessor.UmbracoContext?.PublishedRequest?.PublishedContent);
public Hashtable GetPageElements(int contentId) => this.GetPageElements(this.umbracoContextAccessor.UmbracoContext.Content.GetById(contentId));
public Hashtable GetPageElements(IPublishedContent content)
{
var pageElements = new Hashtable();
if (content != null)
{
pageElements.Add("pageID", content.Id);
pageElements.Add("parentID", content.Parent?.Id ?? -1);
pageElements.Add("pageName", content.Name);
pageElements.Add("nodeType", content.ContentType.Id);
pageElements.Add("nodeTypeAlias", content.ContentType.Alias);
pageElements.Add("writerName", content.WriterName);
pageElements.Add("creatorName", content.CreatorName);
pageElements.Add("createDate", content.CreateDate);
pageElements.Add("updateDate", content.UpdateDate);
pageElements.Add("path", content.Path);
pageElements.Add("splitpath", content.Path.Split(new char[] { ',' }));
if (content.TemplateId is int templateId)
{
pageElements.Add("template", templateId);
}
foreach (var property in content.Properties)
{
if (!pageElements.ContainsKey(property.Alias))
{
pageElements.Add(property.Alias, property.GetSourceValue());
}
}
}
return pageElements;
}
}
[ComposeAfter(typeof(UmbracoFormsComposer))]
public class UmbracoFormsPageServiceComposer : IUserComposer
{
public void Compose(Composition composition)
{
composition.Register<IPageService, PublishedContentPageService>(Lifetime.Singleton);
}
} |
I've updated and extended the above code, so it's also possible to switch to an implementation that uses the preview version of In a private discussion with @HalldorLyngmo, I've recommended to include both implementations and either:
I would go for solution 2, because using |
Regarding the provided implementation, maybe it's better to use |
The following using System.Collections;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
using Umbraco.Forms.Core.Services;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
public class PublishedContentPageService : IPageService
{
protected readonly IUmbracoContextFactory umbracoContextFactory;
protected readonly IUserService userService;
public PublishedContentPageService(IUmbracoContextFactory umbracoContextFactory, IUserService userService)
{
this.umbracoContextFactory = umbracoContextFactory;
this.userService = userService;
}
public Hashtable GetPageElements() => this.GetPageElements(this.GetContent());
public Hashtable GetPageElements(int contentId) => this.GetPageElements(this.GetContent(contentId));
protected virtual IPublishedContent GetContent(int? contentId = null)
{
using (var umbracoContextReference = this.umbracoContextFactory.EnsureUmbracoContext())
{
if (!contentId.HasValue)
{
contentId = umbracoContextReference.UmbracoContext.PublishedRequest?.PublishedContent?.Id;
}
if (contentId.HasValue)
{
return this.GetContent(umbracoContextReference.UmbracoContext.Content, contentId.Value);
}
return null;
}
}
protected virtual IPublishedContent GetContent(IPublishedContentCache contentCache, int contentId) => contentCache.GetById(contentId);
protected virtual Hashtable GetPageElements(IPublishedContent content)
{
var pageElements = new Hashtable();
if (content != null)
{
pageElements.Add("pageID", content.Id);
pageElements.Add("parentID", content.Parent?.Id ?? -1);
pageElements.Add("pageName", content.Name);
pageElements.Add("nodeType", content.ContentType.Id);
pageElements.Add("nodeTypeAlias", content.ContentType.Alias);
pageElements.Add("writerName", content.WriterName(this.userService));
pageElements.Add("creatorName", content.CreatorName(this.userService));
pageElements.Add("createDate", content.CreateDate);
pageElements.Add("updateDate", content.UpdateDate);
pageElements.Add("path", content.Path);
pageElements.Add("splitpath", content.Path.Split(new char[] { ',' }));
if (content.TemplateId is int templateId)
{
pageElements.Add("template", templateId);
}
foreach (var property in content.Properties)
{
if (!pageElements.ContainsKey(property.Alias))
{
pageElements.Add(property.Alias, property.Value());
}
}
}
return pageElements;
}
}
public class PublishedContentPreviewPageService : PublishedContentPageService
{
public PublishedContentPreviewPageService(IUmbracoContextFactory umbracoContextFactory, IUserService userService)
: base(umbracoContextFactory, userService)
{ }
protected override IPublishedContent GetContent(IPublishedContentCache contentCache, int contentId) => contentCache.GetById(true, contentId);
} And this ensures only a single service is registered for using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Forms.Core.Services;
[ComposeAfter(typeof(Umbraco.Forms.Core.Components.UmbracoFormsComposer))]
public class UmbracoFormsComposer : IUserComposer
{
public void Compose(Composition composition)
{
composition.RegisterUnique<IPageService, PublishedContentPageService>();
//composition.RegisterUnique<IPageService, PublishedContentPreviewPageService>();
}
} |
Fixed in this PR https://github.com/umbraco/Forms/pull/443 |
Every page with one or more forms causes the following SQL queries, as it uses the
IContentService
to populate all page elements (to replace page fields like[#pageId]
and[#pageName]
), so the first page view/form render makes quite a lot of SQL queries:Because the
IContentService
/IDocumentRepository
caches the results, the next page views/renders do not generate the same amount of SQL queries, but because the service uses read-locks, it still executes some SQL queries:These SQL timings are from a LocalDb database instance running on a beefy developer PC, so they will be considerable slower on live environments, especially if the database server is not local (e.g. on Umbraco Cloud and Azure).
Reproduction
Bug summary
Umbraco Forms should not use Umbraco services when rendering forms, so the SQL queries can be avoided. Using the content service could even result in using unpublished values to be used/exposed!
Specifics
Running Umbraco 8.6.4 and Umbraco Forms 8.4.2 (both latest versions at the time of writing).
Steps to reproduce
Add a form to a page and enable the MiniProfiler results (append
?umbDebug=true
to the URL).Expected result
Use the Umbraco content cache, so it doesn't execute SQL queries and only uses published content.
Actual result
Every request causes at least read-lock SQL queries to be executed, causing the page to unneccesary slow down.
The text was updated successfully, but these errors were encountered: