Improved Performance of sql queries related to articles
This commit is contained in:
parent
cdd1490801
commit
04bea51271
|
@ -164,25 +164,22 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
protected override void OnInitialized() {
|
||||||
|
using var context = ContextFactory.CreateDbContext();
|
||||||
|
var query = context.Set<Article>()
|
||||||
|
.IgnoreQueryFilters().Where(a => !a.IsDeleted)
|
||||||
|
.Include(a => a.Author)
|
||||||
|
.Include(a => a.Reviewer)
|
||||||
|
.Include(a => a.Categories);
|
||||||
|
|
||||||
// We need blocking calls here, bc otherwise Blazor will execute Render in parallel,
|
// We need blocking calls here, bc otherwise Blazor will execute Render in parallel,
|
||||||
// running into a null pointer on the Article property and panicking
|
// running into a null pointer on the Article property and panicking
|
||||||
if (Id is not null) {
|
if (Id is not null) {
|
||||||
using var context = ContextFactory.CreateDbContext();
|
Article = query.AsSingleQuery().FirstOrDefault(a => a.Id == Id);
|
||||||
Article = context.Set<Article>()
|
|
||||||
.IgnoreQueryFilters().Where(a => !a.IsDeleted)
|
|
||||||
.Include(a => a.Author)
|
|
||||||
.Include(a => a.Reviewer)
|
|
||||||
.Include(a => a.Categories)
|
|
||||||
.FirstOrDefault(a => a.Id == Id);
|
|
||||||
} else if (Date is { } date && Title is { } title) {
|
} else if (Date is { } date && Title is { } title) {
|
||||||
using var context = ContextFactory.CreateDbContext();
|
|
||||||
string? slug = TitleEncoded == null ? null : Uri.EscapeDataString(TitleEncoded);
|
string? slug = TitleEncoded == null ? null : Uri.EscapeDataString(TitleEncoded);
|
||||||
Article ??= context.Set<Article>()
|
Article = query.AsSingleQuery().FirstOrDefault(a =>
|
||||||
.IgnoreQueryFilters().Where(a => !a.IsDeleted)
|
a.PublishDate.Date == date.Date
|
||||||
.Include(a => a.Author)
|
&& (slug != null && a.Slug == slug || a.Title.ToLower() == title));
|
||||||
.Include(a => a.Reviewer)
|
|
||||||
.Include(a => a.Categories)
|
|
||||||
.FirstOrDefault(a => a.PublishDate.Date == date.Date && (slug != null && a.Slug == slug || a.Title.ToLower() == title));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,10 +42,22 @@
|
||||||
protected override async Task OnInitializedAsync() {
|
protected override async Task OnInitializedAsync() {
|
||||||
await using var context = await ContextFactory.CreateDbContextAsync();
|
await using var context = await ContextFactory.CreateDbContextAsync();
|
||||||
string category = WebUtility.UrlDecode(CategoryName);
|
string category = WebUtility.UrlDecode(CategoryName);
|
||||||
await context.Set<Category>().LoadAsync();
|
var now = DateTimeOffset.UtcNow;
|
||||||
|
// First load Category with simple chain and manual filters, as to minimize
|
||||||
|
// filter redundancy and query complexity (category -> Articles -> Author is linear)
|
||||||
Category = await context.Set<Category>()
|
Category = await context.Set<Category>()
|
||||||
.Include(c => c.Articles).ThenInclude(a => a.Author)
|
.IgnoreAutoIncludes().IgnoreQueryFilters()
|
||||||
.Include(c => c.Articles).ThenInclude(a => a.Categories)
|
.Include(c => c.Articles.Where(a => !a.IsDeleted && a.PublishDate <= now))
|
||||||
|
.ThenInclude(a => a.Author)
|
||||||
.FirstOrDefaultAsync(c => c.Name == category);
|
.FirstOrDefaultAsync(c => c.Name == category);
|
||||||
|
// Load all the other categories missing on the articles, by loading all relevant
|
||||||
|
// articles ID with their categories, so EF can map them to the already loaded entries
|
||||||
|
// (again manual filter to minimize redundancy)
|
||||||
|
await context.Set<Article>()
|
||||||
|
.IgnoreAutoIncludes().IgnoreQueryFilters()
|
||||||
|
.Where(a => !a.IsDeleted && a.PublishDate <= now && a.Categories.Contains(Category!))
|
||||||
|
.Select(a => new {
|
||||||
|
a.Id, a.Categories
|
||||||
|
}).LoadAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,11 +161,14 @@
|
||||||
try {
|
try {
|
||||||
await using var context = await ContextFactory.CreateDbContextAsync();
|
await using var context = await ContextFactory.CreateDbContextAsync();
|
||||||
|
|
||||||
var query = context.Set<Article>()
|
var query = context.Set<Article>();
|
||||||
.Include(a => a.Author).Include(a => a.Categories)
|
|
||||||
.OrderByDescending(a => a.PublishDate).ThenBy(a => a.Id);
|
|
||||||
|
|
||||||
Featured = await query.FirstOrDefaultAsync();
|
Featured = await query
|
||||||
|
.Include(a => a.Author)
|
||||||
|
.Include(a => a.Categories)
|
||||||
|
.OrderByDescending(a => a.PublishDate).ThenBy(a => a.Id)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
TotalPages = (int) Math.Max(Math.Ceiling((await query.CountAsync() - 1) / 10.0), 1);
|
TotalPages = (int) Math.Max(Math.Ceiling((await query.CountAsync() - 1) / 10.0), 1);
|
||||||
} catch {
|
} catch {
|
||||||
Message.ShowError(Localizer["Articles_Load_Error"]);
|
Message.ShowError(Localizer["Articles_Load_Error"]);
|
||||||
|
@ -178,6 +181,7 @@
|
||||||
return await context.Set<Article>()
|
return await context.Set<Article>()
|
||||||
.Include(a => a.Author).Include(a => a.Categories)
|
.Include(a => a.Author).Include(a => a.Categories)
|
||||||
.OrderByDescending(a => a.PublishDate).ThenBy(a => a.Id)
|
.OrderByDescending(a => a.PublishDate).ThenBy(a => a.Id)
|
||||||
|
.AsSplitQuery()
|
||||||
.Skip(page + 1).Take(count).ToListAsync();
|
.Skip(page + 1).Take(count).ToListAsync();
|
||||||
} catch {
|
} catch {
|
||||||
Message.ShowError(Localizer["Articles_Load_Error"]);
|
Message.ShowError(Localizer["Articles_Load_Error"]);
|
||||||
|
|
|
@ -93,13 +93,20 @@
|
||||||
|
|
||||||
// Find user
|
// Find user
|
||||||
if (Id is not null) {
|
if (Id is not null) {
|
||||||
User = await context.Users.Include(u => u.Articles)
|
var now = DateTimeOffset.UtcNow;
|
||||||
.ThenInclude(a => a.Categories)
|
User = await context.Users
|
||||||
|
.IgnoreAutoIncludes().IgnoreQueryFilters()
|
||||||
|
.Include(u => u.Articles.Where(a => !a.IsDeleted && a.PublishDate <= now))
|
||||||
.FirstOrDefaultAsync(u => u.Id == Id.ToString());
|
.FirstOrDefaultAsync(u => u.Id == Id.ToString());
|
||||||
|
await context.Set<Article>()
|
||||||
|
.Where(a => a.Author.Id == Id.ToString())
|
||||||
|
.Select(a => new {
|
||||||
|
a.Id, a.Categories
|
||||||
|
}).LoadAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate access to user
|
// Validate access to user
|
||||||
if (User is not null && User.Articles.Count > 0) {
|
if (User is not null && (User.Articles.Count > 0 || HttpContext.User.IsInRole("Admin"))) {
|
||||||
} else if (User is not null && HttpContext.User.FindFirst("Id")?.Value == User.Id) {
|
} else if (User is not null && HttpContext.User.FindFirst("Id")?.Value == User.Id) {
|
||||||
Message.ShowWarning(Localizer["ProfileNotPublic_Message"]);
|
Message.ShowWarning(Localizer["ProfileNotPublic_Message"]);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue