< Summary

Information
Class: Elsa.Persistence.EFCore.Modules.Alterations.AlterationTenantOwnedUpsert
Assembly: Elsa.Persistence.EFCore
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/AlterationTenantOwnedUpsert.cs
Line coverage
100%
Covered lines: 19
Uncovered lines: 0
Coverable lines: 19
Total lines: 62
Line coverage: 100%
Branch coverage
87%
Covered branches: 7
Total branches: 8
Branch coverage: 87.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
StampTenantId(...)100%66100%
AmbientTenantId(...)50%22100%
OwnedId(...)100%11100%
InsertIfAbsentAsync()100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/AlterationTenantOwnedUpsert.cs

#LineLine coverage
 1using System.Linq.Expressions;
 2using Elsa.Common.Entities;
 3using Elsa.Common.Multitenancy;
 4using Microsoft.EntityFrameworkCore;
 5
 6namespace Elsa.Persistence.EFCore.Modules.Alterations;
 7
 8/// <summary>
 9/// Compare-and-swap upsert for Alterations EF writes. Ownership is decided in the UPDATE
 10/// predicate itself (no pre-read guard). A 0-row update falls through to INSERT; a PK
 11/// collision is the unowned-row refuse path.
 12/// </summary>
 13internal static class AlterationTenantOwnedUpsert
 14{
 15    public static void StampTenantId(Entity entity, string? ambientTenantId)
 16    {
 8217        if (entity.TenantId == Tenant.AgnosticTenantId)
 1418            return;
 19
 6820        if (entity.TenantId == null && ambientTenantId != null)
 921            entity.TenantId = ambientTenantId;
 6822    }
 23
 24    public static string AmbientTenantId(AlterationsElsaDbContext dbContext) =>
 8225        dbContext.TenantId ?? Tenant.DefaultTenantId;
 26
 27    /// <summary>
 28    /// EF translation of <see cref="TenantVisibility.CanReplaceOwnedRow"/>. Named UPDATE
 29    /// matches <c>Target.TenantId</c> to the ambient writer, not a forged source
 30    /// <c>TenantId</c>. <c>*</c> updates still require ambient and source <c>*</c>.
 31    /// </summary>
 32    public static Expression<Func<TEntity, bool>> OwnedId<TEntity>(string id, string? sourceTenantId, string ambientTena
 33        where TEntity : Entity
 34    {
 8235        return entity => entity.Id == id && (
 8236            (entity.TenantId == Tenant.AgnosticTenantId && sourceTenantId == Tenant.AgnosticTenantId && ambientTenantId 
 8237            || (entity.TenantId != Tenant.AgnosticTenantId && (
 8238                entity.TenantId == ambientTenantId
 8239                || (entity.TenantId == null && ambientTenantId == Tenant.DefaultTenantId))));
 40    }
 41
 42    public static async Task<bool> InsertIfAbsentAsync<TEntity>(
 43        AlterationsElsaDbContext dbContext,
 44        TEntity entity,
 45        CancellationToken cancellationToken)
 46        where TEntity : Entity
 47    {
 7348        dbContext.Entry(entity).State = EntityState.Added;
 49
 50        try
 51        {
 7352            await dbContext.SaveChangesAsync(cancellationToken);
 5453        }
 1954        catch (DbUpdateException exception) when (DbExceptionClassifier.IsDuplicateKey(exception))
 55        {
 1856            dbContext.Entry(entity).State = EntityState.Detached;
 1857            return false;
 58        }
 59
 5460        return true;
 7261    }
 62}