Optimize invoice performance with indexes and DTO
Added indexes for `ImportedAt` and a composite key on `InvoiceNumber` and `SellerTaxId` to improve query performance. Updated `AppDbContext` and EF Core migrations to reflect these changes. Introduced `ZugferdInvoiceListDto` to optimize memory usage by excluding large fields like `RawXml`. Updated the frontend (`Index.cshtml`) and backend (`Index.cshtml.cs`) to use the new DTO for better performance.
This commit is contained in:
@@ -6,4 +6,27 @@ namespace DXApp.TemplateKitProject.Data;
|
|||||||
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
|
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
|
||||||
{
|
{
|
||||||
public DbSet<ZugferdInvoice> ZugferdInvoices { get; set; }
|
public DbSet<ZugferdInvoice> ZugferdInvoices { get; set; }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity<ZugferdInvoice>(entity =>
|
||||||
|
{
|
||||||
|
// Index für Performance-Optimierung der Rechnungsliste
|
||||||
|
entity.HasIndex(e => e.ImportedAt)
|
||||||
|
.HasDatabaseName("IX_ZugferdInvoices_ImportedAt");
|
||||||
|
|
||||||
|
// Index für Duplikatprüfung
|
||||||
|
entity.HasIndex(e => new { e.InvoiceNumber, e.SellerTaxId })
|
||||||
|
.HasDatabaseName("IX_ZugferdInvoices_InvoiceNumber_SellerTaxId");
|
||||||
|
|
||||||
|
// Decimal-Präzision explizit festlegen (behebt EF Core Warnung)
|
||||||
|
entity.Property(e => e.TotalAmount)
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
entity.Property(e => e.TaxAmount)
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
91
DXApp.TemplateKitProject/Migrations/20260602114123_AddPerformanceIndexes.Designer.cs
generated
Normal file
91
DXApp.TemplateKitProject/Migrations/20260602114123_AddPerformanceIndexes.Designer.cs
generated
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using DXApp.TemplateKitProject.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace DXApp.TemplateKitProject.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20260602114123_AddPerformanceIndexes")]
|
||||||
|
partial class AddPerformanceIndexes
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.8")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("DXApp.TemplateKitProject.Models.ZugferdInvoice", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("BuyerName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("CurrencyCode")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("GuidelineId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Iban")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ImportedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<DateTime>("InvoiceDate")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("InvoiceNumber")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("RawXml")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ResultFilePath")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("SellerName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("SellerTaxId")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("SourceType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<decimal>("TaxAmount")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<decimal>("TotalAmount")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ImportedAt")
|
||||||
|
.HasDatabaseName("IX_ZugferdInvoices_ImportedAt");
|
||||||
|
|
||||||
|
b.HasIndex("InvoiceNumber", "SellerTaxId")
|
||||||
|
.HasDatabaseName("IX_ZugferdInvoices_InvoiceNumber_SellerTaxId");
|
||||||
|
|
||||||
|
b.ToTable("ZugferdInvoices");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace DXApp.TemplateKitProject.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddPerformanceIndexes : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "SellerTaxId",
|
||||||
|
table: "ZugferdInvoices",
|
||||||
|
type: "nvarchar(450)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "nvarchar(max)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "InvoiceNumber",
|
||||||
|
table: "ZugferdInvoices",
|
||||||
|
type: "nvarchar(450)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "nvarchar(max)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ZugferdInvoices_ImportedAt",
|
||||||
|
table: "ZugferdInvoices",
|
||||||
|
column: "ImportedAt");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ZugferdInvoices_InvoiceNumber_SellerTaxId",
|
||||||
|
table: "ZugferdInvoices",
|
||||||
|
columns: new[] { "InvoiceNumber", "SellerTaxId" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_ZugferdInvoices_ImportedAt",
|
||||||
|
table: "ZugferdInvoices");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_ZugferdInvoices_InvoiceNumber_SellerTaxId",
|
||||||
|
table: "ZugferdInvoices");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "SellerTaxId",
|
||||||
|
table: "ZugferdInvoices",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "nvarchar(450)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "InvoiceNumber",
|
||||||
|
table: "ZugferdInvoices",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "nvarchar(450)",
|
||||||
|
oldNullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,7 +49,7 @@ namespace DXApp.TemplateKitProject.Migrations
|
|||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
b.Property<string>("InvoiceNumber")
|
b.Property<string>("InvoiceNumber")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
b.Property<string>("RawXml")
|
b.Property<string>("RawXml")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
@@ -61,7 +61,7 @@ namespace DXApp.TemplateKitProject.Migrations
|
|||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("SellerTaxId")
|
b.Property<string>("SellerTaxId")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
b.Property<string>("SourceType")
|
b.Property<string>("SourceType")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
@@ -74,6 +74,12 @@ namespace DXApp.TemplateKitProject.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ImportedAt")
|
||||||
|
.HasDatabaseName("IX_ZugferdInvoices_ImportedAt");
|
||||||
|
|
||||||
|
b.HasIndex("InvoiceNumber", "SellerTaxId")
|
||||||
|
.HasDatabaseName("IX_ZugferdInvoices_InvoiceNumber_SellerTaxId");
|
||||||
|
|
||||||
b.ToTable("ZugferdInvoices");
|
b.ToTable("ZugferdInvoices");
|
||||||
});
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
|
|||||||
22
DXApp.TemplateKitProject/Models/ZugferdInvoiceListDto.cs
Normal file
22
DXApp.TemplateKitProject/Models/ZugferdInvoiceListDto.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
namespace DXApp.TemplateKitProject.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lightweight DTO für die Rechnungsliste (ohne RawXml für bessere Performance)
|
||||||
|
/// </summary>
|
||||||
|
public class ZugferdInvoiceListDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string InvoiceNumber { get; set; } = string.Empty;
|
||||||
|
public DateTime InvoiceDate { get; set; }
|
||||||
|
public string SellerName { get; set; } = string.Empty;
|
||||||
|
public string SellerTaxId { get; set; } = string.Empty;
|
||||||
|
public string BuyerName { get; set; } = string.Empty;
|
||||||
|
public decimal TotalAmount { get; set; }
|
||||||
|
public decimal TaxAmount { get; set; }
|
||||||
|
public string CurrencyCode { get; set; } = string.Empty;
|
||||||
|
public string Iban { get; set; } = string.Empty;
|
||||||
|
public DateTime ImportedAt { get; set; }
|
||||||
|
public string SourceType { get; set; } = string.Empty;
|
||||||
|
public string ResultFilePath { get; set; } = string.Empty;
|
||||||
|
public string GuidelineId { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@(Html.DevExtreme().DataGrid<DXApp.TemplateKitProject.Models.ZugferdInvoice>()
|
@(Html.DevExtreme().DataGrid<DXApp.TemplateKitProject.Models.ZugferdInvoiceListDto>()
|
||||||
.DataSource(Model.Invoices)
|
.DataSource(Model.Invoices)
|
||||||
.KeyExpr("Id")
|
.KeyExpr("Id")
|
||||||
.ShowBorders(true)
|
.ShowBorders(true)
|
||||||
|
|||||||
@@ -7,12 +7,30 @@ namespace DXApp.TemplateKitProject.Pages.Invoices;
|
|||||||
|
|
||||||
public class IndexModel(AppDbContext db) : PageModel
|
public class IndexModel(AppDbContext db) : PageModel
|
||||||
{
|
{
|
||||||
public List<ZugferdInvoice> Invoices { get; private set; } = [];
|
public List<ZugferdInvoiceListDto> Invoices { get; private set; } = [];
|
||||||
|
|
||||||
public async Task OnGetAsync()
|
public async Task OnGetAsync()
|
||||||
{
|
{
|
||||||
Invoices = await db.ZugferdInvoices
|
Invoices = await db.ZugferdInvoices
|
||||||
.OrderByDescending(i => i.ImportedAt)
|
.OrderByDescending(i => i.ImportedAt)
|
||||||
|
.Select(i => new ZugferdInvoiceListDto
|
||||||
|
{
|
||||||
|
Id = i.Id,
|
||||||
|
InvoiceNumber = i.InvoiceNumber,
|
||||||
|
InvoiceDate = i.InvoiceDate,
|
||||||
|
SellerName = i.SellerName,
|
||||||
|
SellerTaxId = i.SellerTaxId,
|
||||||
|
BuyerName = i.BuyerName,
|
||||||
|
TotalAmount = i.TotalAmount,
|
||||||
|
TaxAmount = i.TaxAmount,
|
||||||
|
CurrencyCode = i.CurrencyCode,
|
||||||
|
Iban = i.Iban,
|
||||||
|
ImportedAt = i.ImportedAt,
|
||||||
|
SourceType = i.SourceType,
|
||||||
|
ResultFilePath = i.ResultFilePath,
|
||||||
|
GuidelineId = i.GuidelineId
|
||||||
|
// RawXml wird NICHT geladen ? Performance-Optimierung!
|
||||||
|
})
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user