using Microsoft.EntityFrameworkCore;
using Prefab.Catalog.Domain.Entities;
using Prefab.Catalog.Domain.Services;
namespace Prefab.Catalog.Data.Services;
///
public sealed class UniqueChecker(ICatalogDbContextFactory dbFactory) : IUniqueChecker
{
private static DbContext AsDbContext(IModuleDbReadOnly db)
{
if (db is not DbContext context)
{
throw new InvalidOperationException("Catalog context must derive from DbContext.");
}
return context;
}
public async Task CategoryNameIsUnique(string name, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(name);
await using var db = await dbFactory.CreateReadOnlyAsync(cancellationToken);
var normalized = name.Trim();
return !await db.Categories
.Where(c => c.DeletedOn == null)
.AnyAsync(c => c.Name == normalized, cancellationToken);
}
public async Task ProductModelNameIsUnique(string name, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(name);
await using var db = await dbFactory.CreateReadOnlyAsync(cancellationToken);
var context = AsDbContext(db);
var normalized = name.Trim().ToUpperInvariant();
return !await context.Set()
.Where(p => p.DeletedOn == null && p.Kind == ProductKind.Model)
.AnyAsync(p => p.Name != null && p.Name.ToUpper() == normalized, cancellationToken);
}
public async Task ProductSlugIsUnique(string? slug, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(slug))
{
return true;
}
await using var db = await dbFactory.CreateReadOnlyAsync(cancellationToken);
var context = AsDbContext(db);
var normalized = slug.Trim().ToLowerInvariant();
return !await context.Set()
.Where(p => p.DeletedOn == null && p.Kind == ProductKind.Model && p.Slug != null)
.AnyAsync(p => p.Slug!.ToLower() == normalized, cancellationToken);
}
public async Task ProductSkuIsUnique(string sku, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(sku);
await using var db = await dbFactory.CreateReadOnlyAsync(cancellationToken);
var context = AsDbContext(db);
var normalized = sku.Trim().ToUpperInvariant();
return !await context.Set()
.Where(p => p.DeletedOn == null && p.Sku != null)
.AnyAsync(p => p.Sku!.ToUpper() == normalized, cancellationToken);
}
public async Task OptionCodeIsUniqueForProduct(Guid productId, string code, CancellationToken cancellationToken = default)
{
if (productId == Guid.Empty)
{
throw new ArgumentException("Product identifier is required.", nameof(productId));
}
ArgumentException.ThrowIfNullOrWhiteSpace(code);
await using var db = await dbFactory.CreateReadOnlyAsync(cancellationToken);
var context = AsDbContext(db);
var normalizedCode = code.Trim().ToUpperInvariant();
return !await context.Set()
.Where(o => o.DeletedOn == null && o.ProductId == productId)
.AnyAsync(o => o.Code.ToUpper() == normalizedCode, cancellationToken);
}
}