Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

# Ignore build artifacts
[Bb]in/
[Oo]bj/
BenchmarkDotNet.Artifacts/
21 changes: 21 additions & 0 deletions backend/Benchmark/Benchmark.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
<PackageReference Include="Moq" Version="4.20.72" />
Comment on lines +12 to +14

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This project references packages that don't appear to be used by the benchmark code (Microsoft.EntityFrameworkCore.InMemory, Moq). Keeping only the required PackageReferences reduces restore/build time and dependency footprint.

Suggested change
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />

Copilot uses AI. Check for mistakes.
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\RESTful API\RESTful API.csproj" />
</ItemGroup>

</Project>
121 changes: 121 additions & 0 deletions backend/Benchmark/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Moq;
using RESTful_API.Models;
using RESTful_API.Service;
using RESTful_API.Interface;
using Microsoft.Data.Sqlite;
Comment on lines +1 to +13

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several using directives here appear unused (e.g., System.Linq, Microsoft.Extensions.Logging, Microsoft.Extensions.Configuration, Moq, RESTful_API.Service, RESTful_API.Interface). Cleaning these up will avoid unnecessary warnings and keeps the benchmark project minimal.

Copilot uses AI. Check for mistakes.
using System.Data.Common;

[MemoryDiagnoser]
public class ServicoInternoBenchmark
{
private PdsContext _context = null!;
private DbConnection _connection = null!;

[GlobalSetup]
public void Setup()
{
_connection = new SqliteConnection("Filename=:memory:");
_connection.Open();

var options = new DbContextOptionsBuilder<PdsContext>()
.UseSqlite(_connection)
.Options;

_context = new PdsContext(options);
_context.Database.EnsureCreated();

_context.Database.ExecuteSqlRaw("PRAGMA foreign_keys = OFF;");

// Seed data
var login = new Login { Idlogin = 1, Email = "test@test.com" };
_context.Logins.Add(login);

for (int i = 1; i <= 200; i++)
{
var cliente = new Cliente { Idcliente = i, NomeCliente = "Cliente " + i, LoginIdlogin = 1 };
_context.Clientes.Add(cliente);

var aluguer1 = new Aluguer {
Idaluguer = i * 2 - 1,
ClienteIdcliente = i,
EstadoAluguer = "Alugado",
DataEntregaPrevista = DateTime.Now.AddDays(-1),
VeiculoIdveiculo = 1,
};

_context.Aluguers.Add(aluguer1);
}
Comment on lines +33 to +55

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PRAGMA foreign_keys = OFF; is being used to allow seeding rows that violate the schema’s foreign keys (e.g., Cliente.CodigoPostalCp defaults to 0, Aluguer.VeiculoIdveiculo = 1 without inserting a matching Veiculo, etc.). This makes the benchmark less representative and can hide issues that would occur in production. Prefer seeding the required related entities and keeping FK enforcement enabled, so inserts and query behavior match real DB constraints.

Copilot uses AI. Check for mistakes.

var loginNav = new Login { Idlogin = 2, Email = "test2@test.com" };
_context.Logins.Add(loginNav);
Comment on lines +57 to +58

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loginNav is created and added to the context but never referenced by the seed data or the benchmarks. Removing unused seeded entities will reduce setup time and keep the benchmark focused on the relationships being measured.

Suggested change
var loginNav = new Login { Idlogin = 2, Email = "test2@test.com" };
_context.Logins.Add(loginNav);

Copilot uses AI. Check for mistakes.
_context.SaveChanges();

_context.ChangeTracker.Clear();
}

[IterationSetup]
public void IterationSetup()
{
_context.ChangeTracker.Clear();
}

[GlobalCleanup]
public void Cleanup()
{
_connection.Dispose();
_context.Dispose();
}

[Benchmark(Baseline = true)]
public async Task NPlusOne()
{
var aluguers = await _context.Aluguers
.Include(a=>a.ClienteIdclienteNavigation)
.Where(a=>a.EstadoAluguer=="Aguarda Levantamento" || a.EstadoAluguer == "Alugado")
.ToListAsync();

foreach (var aluguer in aluguers)
{
if (aluguer.DataEntregaPrevista < DateTime.Now && aluguer.EstadoAluguer == "Alugado")
{
var cliente = await _context.Clientes
.Include(c => c.LoginIdloginNavigation)
.FirstOrDefaultAsync(c => c.Idcliente == aluguer.ClienteIdcliente);
}
}
}

[Benchmark]
public async Task Optimized()
{
var aluguers = await _context.Aluguers
.Include(a=>a.ClienteIdclienteNavigation)
.ThenInclude(c => c.LoginIdloginNavigation)
.Where(a=>a.EstadoAluguer=="Aguarda Levantamento" || a.EstadoAluguer == "Alugado")
.ToListAsync();

foreach (var aluguer in aluguers)
{
if (aluguer.DataEntregaPrevista < DateTime.Now && aluguer.EstadoAluguer == "Alugado")
{
var cliente = aluguer.ClienteIdclienteNavigation;
}
}
Comment on lines +105 to +111

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In both benchmarks, the loaded cliente value is assigned but never used. In the Optimized case, the JIT may be able to eliminate the navigation-property read entirely, which can skew results. Consider consuming a value from cliente (or using BenchmarkDotNet’s Consumer) so the work can’t be optimized away and both benchmarks measure comparable, real work.

Copilot uses AI. Check for mistakes.
}
}

public class Program
{
public static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<ServicoInternoBenchmark>();
}
}
14 changes: 6 additions & 8 deletions backend/RESTful API/Service/ServicoInterno.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,18 @@ public async Task Executar()
{
// Lógica da tarefa interna
var aluguers = await _context.Aluguers
.Include(a=>a.ClienteIdclienteNavigation)
.Where(a=>a.EstadoAluguer=="Aguarda Levantamento" && a.EstadoAluguer == "Alugado")
.Include(a => a.ClienteIdclienteNavigation)
.ThenInclude(c => c.LoginIdloginNavigation)
.Where(a => a.EstadoAluguer == "Aguarda Levantamento" || a.EstadoAluguer == "Alugado")
.ToListAsync();
Comment on lines 38 to 42

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the corrected || condition, this query will now load all Alugado and Aguarda Levantamento rows and then filter in-memory inside the loop. If these tables grow, that can become a significant load even with eager loading. Consider pushing the date predicates into the database query (e.g., only fetch rows that are already overdue / past pickup time) so the service processes a smaller set of records each run.

Copilot uses AI. Check for mistakes.


foreach (var aluguer in aluguers)
{
if (aluguer.DataEntregaPrevista < DateTime.Now && aluguer.EstadoAluguer == "Alugado")
{
var cliente = await _context.Clientes
.Include(c => c.LoginIdloginNavigation)
.FirstOrDefaultAsync(c => c.Idcliente == aluguer.ClienteIdcliente); if (cliente != null )
var cliente = aluguer.ClienteIdclienteNavigation;
if (cliente != null)
{
var email = cliente.LoginIdloginNavigation.Email;
var assunto = "Notificação de Devolução do Veiculo";
Expand All @@ -69,9 +69,7 @@ public async Task Executar()

if (aluguer.DataLevantamento < DateTime.Now && aluguer.EstadoAluguer == "Aguarda Levantamento")
{
var cliente = await _context.Clientes
.Include(c => c.LoginIdloginNavigation)
.FirstOrDefaultAsync(c => c.Idcliente == aluguer.ClienteIdcliente);
var cliente = aluguer.ClienteIdclienteNavigation;

if (cliente != null)
{
Expand Down