performance improvements to reduce http and database interactions

This commit is contained in:
Shaun Walker 2022-08-12 16:47:51 -04:00
parent 4cae3f02ed
commit 3c6ebd7742
9 changed files with 163 additions and 30 deletions

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Oqtane.Models;
namespace Oqtane.UI
@ -8,11 +9,8 @@ namespace Oqtane.UI
{
public Alias Alias { get; set; }
public Site Site { get; set; }
public List<Language> Languages { get; set; }
public List<Page> Pages { get; set; }
public Page Page { get; set; }
public User User { get; set; }
public List<Module> Modules { get; set; }
public Uri Uri { get; set; }
public Dictionary<string, string> QueryString { get; set; }
public string UrlParameters { get; set; }
@ -24,5 +22,18 @@ namespace Oqtane.UI
public int VisitorId { get; set; }
public string RemoteIPAddress { get; set; }
public string ReturnUrl { get; set; }
public List<Page> Pages
{
get { return Site.Pages; }
}
public List<Module> Modules
{
get { return Site.Modules; }
}
public List<Language> Languages
{
get { return Site.Languages; }
}
}
}

View File

@ -7,10 +7,8 @@
@inject INavigationInterception NavigationInterception
@inject ISyncService SyncService
@inject ISiteService SiteService
@inject ILanguageService LanguageService
@inject IPageService PageService
@inject IUserService UserService
@inject IModuleService ModuleService
@inject IUrlMappingService UrlMappingService
@inject ILogService LogService
@inject IJSRuntime JSRuntime
@ -181,14 +179,13 @@
if (PageState == null || refresh == UI.Refresh.Site)
{
languages = await LanguageService.GetLanguagesAsync(site.SiteId);
pages = await PageService.GetPagesAsync(site.SiteId);
pages = pages.Where(item => !item.IsDeleted).ToList();
pages = site.Pages.Where(item => !item.IsDeleted).ToList();
languages = site.Languages;
}
else
{
languages = PageState.Languages;
pages = PageState.Pages;
languages = PageState.Languages;
}
if (PageState == null || refresh == UI.Refresh.Site)
@ -226,8 +223,7 @@
if (PageState == null || refresh == UI.Refresh.Site)
{
modules = await ModuleService.GetModulesAsync(site.SiteId);
modules = modules.Where(item => !item.IsDeleted).ToList();
modules = site.Modules.Where(item => !item.IsDeleted).ToList();
}
else
{
@ -240,11 +236,8 @@
{
Alias = SiteState.Alias,
Site = site,
Languages = languages,
Pages = pages,
Page = page,
User = user,
Modules = modules,
Uri = new Uri(_absoluteUri, UriKind.Absolute),
QueryString = querystring,
UrlParameters = route.UrlParameters,

View File

@ -24,9 +24,9 @@ namespace Oqtane.Controllers
private readonly ILogManager _logger;
private readonly Alias _alias;
public LanguageController(ILanguageRepository language, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
public LanguageController(ILanguageRepository languages, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
{
_languages = language;
_languages = languages;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();

View File

@ -8,6 +8,11 @@ using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using System.Net;
using Oqtane.Security;
using System.Globalization;
using Microsoft.Extensions.Caching.Memory;
using Oqtane.Extensions;
using System;
namespace Oqtane.Controllers
{
@ -15,17 +20,31 @@ namespace Oqtane.Controllers
public class SiteController : Controller
{
private readonly ISiteRepository _sites;
private readonly IPageRepository _pages;
private readonly IModuleRepository _modules;
private readonly IPageModuleRepository _pageModules;
private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly ILanguageRepository _languages;
private readonly IUserPermissions _userPermissions;
private readonly ISettingRepository _settings;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly IMemoryCache _cache;
private readonly Alias _alias;
public SiteController(ISiteRepository sites, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
public SiteController(ISiteRepository sites, IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IMemoryCache cache)
{
_sites = sites;
_pages = pages;
_modules = modules;
_pageModules = pageModules;
_moduleDefinitions = moduleDefinitions;
_languages = languages;
_userPermissions = userPermissions;
_settings = settings;
_syncManager = syncManager;
_logger = logger;
_cache = cache;
_alias = tenantManager.GetAlias();
}
@ -41,17 +60,92 @@ namespace Oqtane.Controllers
[HttpGet("{id}")]
public Site Get(int id)
{
var site = _sites.GetSite(id);
if (!User.Identity.IsAuthenticated)
{
return _cache.GetOrCreate($"site:{HttpContext.GetAlias().SiteKey}", entry =>
{
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
return GetSite(id);
});
}
else
{
return GetSite(id);
}
}
private Site GetSite(int siteid)
{
var site = _sites.GetSite(siteid);
if (site.SiteId == _alias.SiteId)
{
// site settings
site.Settings = _settings.GetSettings(EntityNames.Site, site.SiteId)
.Where(item => !item.IsPrivate || User.IsInRole(RoleNames.Admin))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
// pages
List<Setting> settings = _settings.GetSettings(EntityNames.Page).ToList();
site.Pages = new List<Page>();
foreach (Page page in _pages.GetPages(site.SiteId))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions))
{
page.Settings = settings.Where(item => item.EntityId == page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
site.Pages.Add(page);
}
}
// modules
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(site.SiteId).ToList();
settings = _settings.GetSettings(EntityNames.Module).ToList();
site.Modules = new List<Module>();
foreach (PageModule pagemodule in _pageModules.GetPageModules(site.SiteId))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions))
{
Module module = new Module();
module.SiteId = pagemodule.Module.SiteId;
module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName;
module.AllPages = pagemodule.Module.AllPages;
module.Permissions = pagemodule.Module.Permissions;
module.CreatedBy = pagemodule.Module.CreatedBy;
module.CreatedOn = pagemodule.Module.CreatedOn;
module.ModifiedBy = pagemodule.Module.ModifiedBy;
module.ModifiedOn = pagemodule.Module.ModifiedOn;
module.DeletedBy = pagemodule.DeletedBy;
module.DeletedOn = pagemodule.DeletedOn;
module.IsDeleted = pagemodule.IsDeleted;
module.PageModuleId = pagemodule.PageModuleId;
module.ModuleId = pagemodule.ModuleId;
module.PageId = pagemodule.PageId;
module.Title = pagemodule.Title;
module.Pane = pagemodule.Pane;
module.Order = pagemodule.Order;
module.ContainerType = pagemodule.ContainerType;
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
site.Modules.Add(module);
}
}
// languages
site.Languages = _languages.GetLanguages(site.SiteId).ToList();
var defaultCulture = CultureInfo.GetCultureInfo(Constants.DefaultCulture);
site.Languages.Add(new Language { Code = defaultCulture.Name, Name = defaultCulture.DisplayName, Version = Constants.Version, IsDefault = !site.Languages.Any(l => l.IsDefault) });
return site;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Get Attempt {SiteId}", id);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Get Attempt {SiteId}", siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}

View File

@ -1,17 +1,21 @@
using Microsoft.Extensions.Caching.Memory;
using Oqtane.Models;
using Oqtane.Shared;
using System;
using System.Collections.Generic;
using System.Linq;
using Oqtane.Repository;
using System.Reflection;
namespace Oqtane.Infrastructure
{
public class SyncManager : ISyncManager
{
private readonly IMemoryCache _cache;
private List<SyncEvent> SyncEvents { get; set; }
public SyncManager()
public SyncManager(IMemoryCache cache)
{
_cache = cache;
SyncEvents = new List<SyncEvent>();
}
@ -28,6 +32,10 @@ namespace Oqtane.Infrastructure
public void AddSyncEvent(int tenantId, string entityName, int entityId, bool reload)
{
SyncEvents.Add(new SyncEvent { TenantId = tenantId, EntityName = entityName, EntityId = entityId, Reload = reload, ModifiedOn = DateTime.UtcNow });
if (entityName == EntityNames.Site)
{
_cache.Remove($"site:{tenantId}:{entityId}");
}
// trim sync events
SyncEvents.RemoveAll(item => item.ModifiedOn < DateTime.UtcNow.AddHours(-1));
}

View File

@ -1,6 +1,9 @@
using System.Linq;
using Oqtane.Documentation;
using System.Collections.Generic;
using Microsoft.Extensions.Caching.Memory;
using Oqtane.Infrastructure;
using System;
namespace Oqtane.Modules.HtmlText.Repository
{
@ -8,15 +11,23 @@ namespace Oqtane.Modules.HtmlText.Repository
public class HtmlTextRepository : IHtmlTextRepository, ITransientService
{
private readonly HtmlTextContext _db;
private readonly IMemoryCache _cache;
private readonly SiteState _siteState;
public HtmlTextRepository(HtmlTextContext context)
public HtmlTextRepository(HtmlTextContext context, IMemoryCache cache, SiteState siteState)
{
_db = context;
_cache = cache;
_siteState = siteState;
}
public IEnumerable<Models.HtmlText> GetHtmlTexts(int moduleId)
{
return _db.HtmlText.Where(item => item.ModuleId == moduleId);
return _cache.GetOrCreate($"HtmlText:{_siteState.Alias.SiteKey}:{moduleId}", entry =>
{
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
return _db.HtmlText.Where(item => item.ModuleId == moduleId).ToList();
});
}
public Models.HtmlText GetHtmlText(int htmlTextId)
@ -28,6 +39,7 @@ namespace Oqtane.Modules.HtmlText.Repository
{
_db.HtmlText.Add(htmlText);
_db.SaveChanges();
ClearCache(htmlText.ModuleId);
return htmlText;
}
@ -35,7 +47,13 @@ namespace Oqtane.Modules.HtmlText.Repository
{
Models.HtmlText htmlText = _db.HtmlText.FirstOrDefault(item => item.HtmlTextId == htmlTextId);
if (htmlText != null) _db.HtmlText.Remove(htmlText);
ClearCache(htmlText.ModuleId);
_db.SaveChanges();
}
private void ClearCache(int moduleId)
{
_cache.Remove($"HtmlText:{_siteState.Alias.SiteKey}:{moduleId}");
}
}
}

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Oqtane.Extensions;
using Oqtane.Models;

View File

@ -109,7 +109,6 @@ namespace Oqtane.Models
#endregion
#region IModuleControl properties
// TODO: unclear why these are IModuleControl properties - there is no such interface
[NotMapped]
public SecurityAccessLevel SecurityAccessLevel { get; set; }
[NotMapped]

View File

@ -83,6 +83,18 @@ namespace Oqtane.Models
/// </summary>
public string Version { get; set; }
[NotMapped]
public Dictionary<string, string> Settings { get; set; }
[NotMapped]
public List<Page> Pages { get; set; }
[NotMapped]
public List<Module> Modules { get; set; }
[NotMapped]
public List<Language> Languages { get; set; }
#region IAuditable Properties
/// <inheritdoc/>
@ -101,17 +113,16 @@ namespace Oqtane.Models
public string DeletedBy { get; set; }
public DateTime? DeletedOn { get; set; }
public bool IsDeleted { get; set; }
#endregion
[NotMapped]
public string SiteTemplateType { get; set; }
[NotMapped]
public Dictionary<string, string> Settings { get; set; }
#region Obsolete properties
[NotMapped]
[Obsolete("This property is deprecated.", false)]
public string DefaultLayoutType { get; set; }
#endregion
}
}