< Summary

Information
Class: Elsa.Persistence.EFCore.Modules.Alterations.EFCoreAlterationPlanStore
Assembly: Elsa.Persistence.EFCore
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/AlterationPlanStore.cs
Line coverage
95%
Covered lines: 58
Uncovered lines: 3
Coverable lines: 61
Total lines: 140
Line coverage: 95%
Branch coverage
91%
Covered branches: 11
Total branches: 12
Branch coverage: 91.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
.ctor(...)100%11100%
SaveAsync()100%1010100%
UpdateOwnedAsync()100%11100%
FindAsync()100%11100%
CountAsync()100%11100%
OnSave(...)100%11100%
OnSaveAsync(...)100%11100%
OnLoadAsync(...)50%2285.71%

File(s)

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

#LineLine coverage
 1using System.Diagnostics.CodeAnalysis;
 2using System.Text.Json;
 3using Elsa.Alterations.Core.Contracts;
 4using Elsa.Alterations.Core.Entities;
 5using Elsa.Alterations.Core.Filters;
 6using Elsa.Alterations.Core.Models;
 7using Elsa.Alterations.Core.Stores;
 8using Elsa.Tenants.Options;
 9using Microsoft.EntityFrameworkCore;
 10using Microsoft.Extensions.DependencyInjection;
 11using Microsoft.Extensions.Options;
 12
 13namespace Elsa.Persistence.EFCore.Modules.Alterations;
 14
 15/// <summary>
 16/// An EF Core implementation of <see cref="IAlterationPlanStore"/>.
 17/// </summary>
 18public class EFCoreAlterationPlanStore : IAlterationPlanStore
 19{
 20    private readonly EntityStore<AlterationsElsaDbContext, AlterationPlan> _store;
 21    private readonly IAlterationSerializer _alterationSerializer;
 22    private readonly bool _tenantEnabled;
 23
 24    /// <summary>
 25    /// Constructor.
 26    /// </summary>
 27    public EFCoreAlterationPlanStore(
 28        EntityStore<AlterationsElsaDbContext, AlterationPlan> store,
 29        IAlterationSerializer alterationSerializer)
 030        : this(store, alterationSerializer, Options.Create(new TenantsOptions()))
 31    {
 032    }
 33
 34    /// <summary>
 35    /// Constructor used by dependency injection. Direct construction through the legacy
 36    /// overload keeps tenancy-aware upsert disabled for compatibility.
 37    /// </summary>
 38    [ActivatorUtilitiesConstructor]
 44539    public EFCoreAlterationPlanStore(
 44540        EntityStore<AlterationsElsaDbContext, AlterationPlan> store,
 44541        IAlterationSerializer alterationSerializer,
 44542        IOptions<TenantsOptions> tenantsOptions)
 43    {
 44544        _store = store;
 44545        _alterationSerializer = alterationSerializer;
 44546        _tenantEnabled = tenantsOptions.Value.IsEnabled;
 44547    }
 48
 49    /// <inheritdoc />
 50    public async Task SaveAsync(AlterationPlan record, CancellationToken cancellationToken = default)
 51    {
 4252        if (!_tenantEnabled)
 53        {
 454            await _store.SaveAsync(record, OnSaveAsync, cancellationToken);
 455            return;
 56        }
 57
 3858        await using var dbContext = await _store.CreateDbContextAsync(cancellationToken);
 3859        var ambientTenantId = AlterationTenantOwnedUpsert.AmbientTenantId(dbContext);
 3860        AlterationTenantOwnedUpsert.StampTenantId(record, ambientTenantId);
 3861        OnSave(dbContext, record);
 62
 3863        var status = record.Status;
 3864        var createdAt = record.CreatedAt;
 3865        var startedAt = record.StartedAt;
 3866        var completedAt = record.CompletedAt;
 3867        var serializedAlterations = dbContext.Entry(record).Property<string>("SerializedAlterations").CurrentValue;
 3868        var serializedFilter = dbContext.Entry(record).Property<string>("SerializedWorkflowInstanceFilter").CurrentValue
 69
 3870        var query = dbContext.Set<AlterationPlan>()
 3871            .IgnoreQueryFilters()
 3872            .Where(AlterationTenantOwnedUpsert.OwnedId<AlterationPlan>(record.Id, record.TenantId, ambientTenantId));
 73
 74        // Inline lambda so net8/net9 bind SetPropertyCalls and net10 binds UpdateSettersBuilder.
 4775        Task<int> UpdateOwnedAsync() => query.ExecuteUpdateAsync(
 4776            setters => setters
 4777                .SetProperty(plan => plan.Status, status)
 4778                .SetProperty(plan => plan.CreatedAt, createdAt)
 4779                .SetProperty(plan => plan.StartedAt, startedAt)
 4780                .SetProperty(plan => plan.CompletedAt, completedAt)
 4781                .SetProperty(plan => EF.Property<string>(plan, "SerializedAlterations"), serializedAlterations)
 4782                .SetProperty(plan => EF.Property<string>(plan, "SerializedWorkflowInstanceFilter"), serializedFilter),
 4783            cancellationToken);
 84
 3885        var updated = await _store.ExecuteWithDbExceptionHandlingAsync(UpdateOwnedAsync, cancellationToken);
 86
 3787        if (updated == 0)
 88        {
 3389            var inserted = await _store.ExecuteWithDbExceptionHandlingAsync(
 3390                () => AlterationTenantOwnedUpsert.InsertIfAbsentAsync(dbContext, record, cancellationToken),
 3391                cancellationToken);
 3392            if (!inserted)
 93            {
 994                var retried = await _store.ExecuteWithDbExceptionHandlingAsync(UpdateOwnedAsync, cancellationToken);
 95
 996                if (retried == 0)
 897                    throw AlterationStoreConflict.HiddenPlanId(record.Id);
 98            }
 99        }
 33100    }
 101
 102    /// <inheritdoc />
 103    [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Deserialize<TValue>(String, JsonSerializerOptions)"
 104    public async Task<AlterationPlan?> FindAsync(AlterationPlanFilter filter, CancellationToken cancellationToken = defa
 105    {
 32106        return await _store.FindAsync(filter.Apply, OnLoadAsync, cancellationToken);
 32107    }
 108
 109    /// <inheritdoc />
 110    public async Task<long> CountAsync(AlterationPlanFilter filter, CancellationToken cancellationToken = default)
 111    {
 7112        return await _store.CountAsync(filter.Apply, cancellationToken);
 7113    }
 114
 115    private void OnSave(AlterationsElsaDbContext elsaDbContext, AlterationPlan entity)
 116    {
 42117        elsaDbContext.Entry(entity).Property("SerializedAlterations").CurrentValue = _alterationSerializer.SerializeMany
 42118        elsaDbContext.Entry(entity).Property("SerializedWorkflowInstanceFilter").CurrentValue = JsonSerializer.Serialize
 42119    }
 120
 121    private ValueTask OnSaveAsync(AlterationsElsaDbContext dbContext, AlterationPlan entity, CancellationToken cancellat
 122    {
 4123        OnSave(dbContext, entity);
 4124        return default;
 125    }
 126
 127    [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Deserialize<TValue>(String, JsonSerializerOptions)"
 128    private ValueTask OnLoadAsync(AlterationsElsaDbContext elsaDbContext, AlterationPlan? entity, CancellationToken canc
 129    {
 23130        if (entity is null)
 0131            return default;
 132
 23133        var alterationsJson = elsaDbContext.Entry(entity).Property<string>("SerializedAlterations").CurrentValue;
 23134        var workflowInstanceFilterJson = elsaDbContext.Entry(entity).Property<string>("SerializedWorkflowInstanceFilter"
 23135        entity.Alterations = _alterationSerializer.DeserializeMany(alterationsJson).ToList();
 23136        entity.WorkflowInstanceFilter = JsonSerializer.Deserialize<AlterationWorkflowInstanceFilter>(workflowInstanceFil
 137
 23138        return default;
 139    }
 140}