< Summary

Information
Class: ArturRios.Data.Relational.Core.Repositories.EfRepository<T>
Assembly: ArturRios.Data.Relational.Core
File(s): /home/runner/work/dotnet-data/dotnet-data/src/ArturRios.Data.Relational.Core/Repositories/EfRepository.cs
Line coverage
80%
Covered lines: 84
Uncovered lines: 20
Coverable lines: 104
Total lines: 208
Line coverage: 80.7%
Branch coverage
75%
Covered branches: 3
Total branches: 4
Branch coverage: 75%
Method coverage
90%
Covered methods: 20
Fully covered methods: 18
Total methods: 22
Method coverage: 90.9%
Full method coverage: 81.8%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Set()100%11100%
GetAllAsync(...)100%11100%
GetByIdAsync(...)100%11100%
CreateAsync(...)100%11100%
CreateRangeAsync(...)100%11100%
UpdateAsync(...)100%11100%
UpdateRangeAsync(...)100%210%
DeleteAsync(...)100%11100%
DeleteRangeAsync(...)100%11100%
Query()100%11100%
GetAll()100%11100%
GetById(...)100%11100%
Create(...)100%11100%
CreateRange(...)100%22100%
Update(...)100%11100%
UpdateRange(...)100%210%
Delete(...)100%11100%
DeleteRange(...)50%22100%
Fail(...)100%11100%
Guarded(...)100%1160%
GuardedAsync()100%1140%

File(s)

/home/runner/work/dotnet-data/dotnet-data/src/ArturRios.Data.Relational.Core/Repositories/EfRepository.cs

#LineLine coverage
 1using ArturRios.Data.Relational.Core.Configuration;
 2using ArturRios.Data.Relational.Core.Entities;
 3using ArturRios.Data.Relational.Core.Interfaces;
 4using ArturRios.Output;
 5using Microsoft.EntityFrameworkCore;
 6
 7namespace ArturRios.Data.Relational.Core.Repositories;
 8
 9/// <summary>
 10///     Provider-agnostic Entity Framework Core implementation of the repository contracts.
 11///     Every write auto-saves; inside an active unit-of-work transaction,
 12///     saves flush without committing. Infrastructure failures are returned as <see cref="DataOutput{T}" /> errors.
 13/// </summary>
 14/// <typeparam name="T">The entity type.</typeparam>
 15/// <param name="context">The application's <see cref="BaseDbContext" />.</param>
 7416public class EfRepository<T>(BaseDbContext context) : IRepository<T>, IAsyncRepository<T>
 17    where T : Entity
 18{
 19    /// <summary>Message returned when an optimistic-concurrency conflict is detected.</summary>
 20    protected const string ConcurrencyMessage = RelationalErrors.ConcurrencyMessage;
 21
 22    /// <summary>Message returned when a persistence operation fails with no finer classification.</summary>
 23    protected const string PersistenceMessage = RelationalErrors.GenericMessage;
 24
 25    /// <summary>Message returned when a write violates a unique constraint.</summary>
 26    protected const string UniqueViolationMessage = RelationalErrors.UniqueViolationMessage;
 27
 28    /// <summary>The tracked entity set for <typeparamref name="T" />.</summary>
 12029    protected DbSet<T> Set => context.Set<T>();
 30
 31    /// <inheritdoc />
 32    public Task<DataOutput<IEnumerable<T>>> GetAllAsync(CancellationToken ct = default) =>
 1233        GuardedAsync<IEnumerable<T>>(async () => await Set.ToListAsync(ct).ConfigureAwait(false));
 34
 35    /// <inheritdoc />
 36    public Task<DataOutput<T?>> GetByIdAsync(long id, CancellationToken ct = default) =>
 837        GuardedAsync(async () => await Set.FirstOrDefaultAsync(e => e.Id == id, ct).ConfigureAwait(false));
 38
 39    /// <inheritdoc />
 40    public Task<DataOutput<long>> CreateAsync(T entity, CancellationToken ct = default) =>
 1041        GuardedAsync(async () =>
 1042        {
 1043            await Set.AddAsync(entity, ct).ConfigureAwait(false);
 1044            await context.SaveChangesAsync(ct).ConfigureAwait(false);
 1045
 1046            return entity.Id;
 2047        });
 48
 49    /// <inheritdoc />
 50    public Task<DataOutput<IEnumerable<long>>>
 51        CreateRangeAsync(IEnumerable<T> entities, CancellationToken ct = default) =>
 452        GuardedAsync<IEnumerable<long>>(async () =>
 453        {
 454            var list = entities.ToList();
 455            await Set.AddRangeAsync(list, ct).ConfigureAwait(false);
 456            await context.SaveChangesAsync(ct).ConfigureAwait(false);
 457
 1258            return list.Select(e => e.Id).ToList();
 859        });
 60
 61    /// <inheritdoc />
 62    public Task<DataOutput<T>> UpdateAsync(T entity, CancellationToken ct = default) =>
 263        GuardedAsync(async () =>
 264        {
 265            Set.Update(entity);
 266            await context.SaveChangesAsync(ct).ConfigureAwait(false);
 267
 268            return entity;
 469        });
 70
 71    /// <inheritdoc />
 72    public Task<DataOutput<IEnumerable<T>>> UpdateRangeAsync(IEnumerable<T> entities, CancellationToken ct = default) =>
 073        GuardedAsync<IEnumerable<T>>(async () =>
 074        {
 075            var list = entities.ToList();
 076            Set.UpdateRange(list);
 077            await context.SaveChangesAsync(ct).ConfigureAwait(false);
 078
 079            return list;
 080        });
 81
 82    /// <inheritdoc />
 83    public Task<DataOutput<long>> DeleteAsync(T entity, CancellationToken ct = default) =>
 284        GuardedAsync(async () =>
 285        {
 286            Set.Remove(entity);
 287            await context.SaveChangesAsync(ct).ConfigureAwait(false);
 288
 289            return entity.Id;
 490        });
 91
 92    /// <inheritdoc />
 93    public Task<DataOutput<IEnumerable<long>>> DeleteRangeAsync(IEnumerable<long> ids, CancellationToken ct = default) =
 294        GuardedAsync<IEnumerable<long>>(async () =>
 295        {
 296            var idList = ids.ToList();
 297            var matches = await Set.Where(e => idList.Contains(e.Id)).ToListAsync(ct).ConfigureAwait(false);
 298            Set.RemoveRange(matches);
 299            await context.SaveChangesAsync(ct).ConfigureAwait(false);
 2100
 6101            return matches.Select(e => e.Id).ToList();
 4102        });
 103
 104    /// <inheritdoc cref="Query" />
 2105    public IQueryable<T> Query() => Set.AsQueryable();
 106
 107    /// <inheritdoc />
 108    public DataOutput<IEnumerable<T>> GetAll() =>
 28109        Guarded(IEnumerable<T> () => Set.ToList());
 110
 111    /// <inheritdoc />
 112    public DataOutput<T?> GetById(long id) =>
 32113        Guarded(() => Set.FirstOrDefault(e => e.Id == id));
 114
 115    /// <inheritdoc />
 34116    public DataOutput<long> Create(T entity) => Guarded(() =>
 34117    {
 34118        Set.Add(entity);
 34119        context.SaveChanges();
 30120        return entity.Id;
 34121    });
 122
 123    /// <inheritdoc />
 6124    public DataOutput<IEnumerable<long>> CreateRange(IEnumerable<T> entities) => Guarded(IEnumerable<long> () =>
 6125    {
 6126        var list = entities.ToList();
 6127        Set.AddRange(list);
 6128        context.SaveChanges();
 18129        return list.Select(e => e.Id).ToList();
 6130    });
 131
 132    /// <inheritdoc />
 10133    public DataOutput<T> Update(T entity) => Guarded(() =>
 10134    {
 10135        Set.Update(entity);
 10136        context.SaveChanges();
 6137        return entity;
 10138    });
 139
 140    /// <inheritdoc />
 0141    public DataOutput<IEnumerable<T>> UpdateRange(IEnumerable<T> entities) => Guarded(IEnumerable<T> () =>
 0142    {
 0143        var list = entities.ToList();
 0144        Set.UpdateRange(list);
 0145        context.SaveChanges();
 0146        return list;
 0147    });
 148
 149    /// <inheritdoc />
 2150    public DataOutput<long> Delete(T entity) => Guarded(() =>
 2151    {
 2152        Set.Remove(entity);
 2153        context.SaveChanges();
 2154        return entity.Id;
 2155    });
 156
 157    /// <inheritdoc />
 2158    public DataOutput<IEnumerable<long>> DeleteRange(IEnumerable<long> ids) => Guarded(IEnumerable<long> () =>
 2159    {
 2160        var idList = ids.ToList();
 2161        var matches = Set.Where(e => idList.Contains(e.Id)).ToList();
 2162        Set.RemoveRange(matches);
 2163        context.SaveChanges();
 6164        return matches.Select(e => e.Id).ToList();
 2165    });
 166
 167    /// <summary>
 168    ///     Maps an exception caught by a guard to an error envelope. Provider text - constraint
 169    ///     and index names, columns, SQL fragments, conflicting values - is classified but never
 170    ///     returned; EF Core logs the full exception for operators.
 171    /// </summary>
 172    private static DataOutput<TResult> Fail<TResult>(Exception ex) =>
 12173        DataOutput<TResult>.New.WithError(RelationalErrors.Describe(ex));
 174
 175    /// <summary>Runs a synchronous data operation, converting failures to envelope errors.</summary>
 176    protected static DataOutput<TResult> Guarded<TResult>(Func<TResult> operation)
 177    {
 178        try
 179        {
 84180            return DataOutput<TResult>.New.WithData(operation());
 181        }
 0182        catch (OperationCanceledException)
 183        {
 0184            throw;
 185        }
 186        catch (Exception ex)
 187        {
 12188            return Fail<TResult>(ex);
 189        }
 84190    }
 191
 192    /// <summary>Runs an asynchronous data operation, converting failures to envelope errors.</summary>
 193    protected static async Task<DataOutput<TResult>> GuardedAsync<TResult>(Func<Task<TResult>> operation)
 194    {
 195        try
 196        {
 30197            return DataOutput<TResult>.New.WithData(await operation().ConfigureAwait(false));
 198        }
 0199        catch (OperationCanceledException)
 200        {
 0201            throw;
 202        }
 203        catch (Exception ex)
 204        {
 0205            return Fail<TResult>(ex);
 206        }
 30207    }
 208}