• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lucaslorentz / auto-compute / 23084474323

14 Mar 2026 08:40AM UTC coverage: 59.695%. First build
23084474323

Pull #19

github

lucaslorentz
Add automatic backfill SQL generation in migrations for computed properties

When a computed property is added or its expression changes, `dotnet ef migrations add`
now automatically generates `migrationBuilder.Sql("UPDATE ...")` to update existing records.

- SHA256 hash of expanded expression stored as permanent annotation for change detection
- Uses ExecuteUpdate + SqlCaptureInterceptor for provider-agnostic SQL generation
- Configurable via `EnableBackfillInMigrations` (enabled by default)
- Untranslatable expressions generate a friendly SQL comment
- Existing properties without hashes are skipped (assumes consistency)
- Removes code coverage threshold from CI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pull Request #19: Automatic backfill SQL generation in migrations

21 of 148 new or added lines in 8 files covered. (14.19%)

1995 of 3342 relevant lines covered (59.69%)

663.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/LLL.AutoCompute.EFCore/Internal/SqlCaptureInterceptor.cs
1
using System.Data.Common;
2
using Microsoft.EntityFrameworkCore.Diagnostics;
3

4
namespace LLL.AutoCompute.EFCore.Internal;
5

6
public class SqlCaptureInterceptor : DbCommandInterceptor
7
{
8
    [ThreadStatic]
9
    private static bool _capturing;
10

11
    [ThreadStatic]
12
    public static string? CapturedSql;
13

NEW
14
    public static IDisposable StartCapture() => new CaptureScope();
×
15

16
    public override InterceptionResult<int> NonQueryExecuting(
17
        DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
18
    {
NEW
19
        if (_capturing)
×
20
        {
NEW
21
            CapturedSql = command.CommandText;
×
NEW
22
            return InterceptionResult<int>.SuppressWithResult(0);
×
23
        }
NEW
24
        return result;
×
25
    }
26

27
    public override ValueTask<InterceptionResult<int>> NonQueryExecutingAsync(
28
        DbCommand command, CommandEventData eventData, InterceptionResult<int> result,
29
        CancellationToken cancellationToken = default)
30
    {
NEW
31
        if (_capturing)
×
32
        {
NEW
33
            CapturedSql = command.CommandText;
×
NEW
34
            return ValueTask.FromResult(InterceptionResult<int>.SuppressWithResult(0));
×
35
        }
NEW
36
        return ValueTask.FromResult(result);
×
37
    }
38

39
    public override InterceptionResult<DbDataReader> ReaderExecuting(
40
        DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
41
    {
NEW
42
        if (_capturing)
×
43
        {
NEW
44
            CapturedSql = command.CommandText;
×
45
        }
NEW
46
        return result;
×
47
    }
48

49
    private class CaptureScope : IDisposable
50
    {
NEW
51
        public CaptureScope()
×
52
        {
NEW
53
            _capturing = true;
×
NEW
54
            CapturedSql = null;
×
55
        }
56

57
        public void Dispose()
58
        {
NEW
59
            _capturing = false;
×
60
        }
61
    }
62
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc