tg-me.com/csharp_1001_notes/401
Last Update:
ΠΠ΅ΡΠ΅Ρ
Π²Π°ΡΡΠΈΠΊΠΈ Π² EF Core ΠΏΠΎΠΌΠΎΠ³Π°ΡΡ ΠΏΠ΅ΡΠ΅Ρ
Π²Π°ΡΡΠ²Π°ΡΡ, ΠΈΠ·ΠΌΠ΅Π½ΡΡΡ ΠΈΠ»ΠΈ ΠΏΠΎΠ΄Π°Π²Π»ΡΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ EF Core.
ΠΠ΅ΡΠ΅Ρ
Π²Π°ΡΡΠΈΠΊΠΈ ΡΠ΅Π³ΠΈΡΡΡΠΈΡΡΡΡΡΡ Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠ° DbContext ΠΏΡΠΈ Π½Π°ΡΡΡΠΎΠΉΠΊΠ΅ ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠ°. ΠΠ°ΠΆΠ΄ΡΠΉ ΠΏΠ΅ΡΠ΅Ρ
Π²Π°ΡΡΠΈΠΊ ΡΠ΅Π°Π»ΠΈΠ·ΡΠ΅Ρ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ IInterceptor. ΠΠ΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΠ°ΡΠΏΡΠΎΡΡΡΠ°Π½ΡΠ½Π½ΡΡ
ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄Π½ΡΡ
ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΠΎΠ² Π²ΠΊΠ»ΡΡΠ°ΡΡ- IDbCommandInterceptor,
- IDbConnectionInterceptor,
- IDbTransactionInterceptor,
- ISaveChangesInterceptor.
ΠΠ°ΠΌ Π½Π΅ Π½ΠΊΠΆΠ½ΠΎ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²ΡΠ²Π°ΡΡ ΡΡΠΈ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΡ Π½Π°ΠΏΡΡΠΌΡΡ.
ΠΡΡΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΡΠ΅ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΠΈ ΠΈ ΠΏΠ΅ΡΠ΅ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ.
ΠΠΎΡ Π²Π°ΡΠΈΠ°Π½Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΠΏΠ΅ΡΠ΅Ρ
Π²Π°ΡΡΠΈΠΊΠΎΠ² Ρ ΠΏΠΎΠΌΠΎΡΡΡ ΡΠ°ΠΌΠΎΠ³ΠΎ ΡΠ°ΡΠΏΡΠΎΡΡΡΠ°Π½ΡΠ½Π½ΠΎΠ³ΠΎ ΠΏΠ΅ΡΠ΅Ρ
Π²Π°ΡΡΠΈΠΊΠ° SaveChangesInterceptor
, ΠΊΠΎΡΠΎΡΡΠΉ Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅Ρ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ ΠΏΡΠΈ ΡΠΎΡ
ΡΠ°Π½Π΅Π½ΠΈΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ Π² Π±Π°Π·Π΅ Π΄Π°Π½Π½ΡΡ
.
ΠΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π·Π°ΠΏΠΈΡΠ΅ΠΉ Π°ΡΠ΄ΠΈΡΠ°
ΠΠ°ΠΏΠΈΡΠΈ Π°ΡΠ΄ΠΈΡΠ° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ - ΡΠ΅Π½Π½Π°Ρ ΡΡΠ½ΠΊΡΠΈΡ Π² Π½Π΅ΠΊΠΎΡΠΎΡΡΡ
ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡΡ
. ΠΡ Π·Π°ΠΏΠΈΡΡΠ²Π°Π΅ΡΠ΅ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ Π°ΡΠ΄ΠΈΡΠ° ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΠ°Π·, ΠΊΠΎΠ³Π΄Π° ΠΎΠ±ΡΠ΅ΠΊΡ ΡΠΎΠ·Π΄Π°ΡΡΡΡ ΠΈΠ»ΠΈ ΠΈΠ·ΠΌΠ΅Π½ΡΠ΅ΡΡΡ. Π’Π°ΠΊΠΆΠ΅ ΡΡΠΎ ΠΌΠΎΠ³ΡΡ Π±ΡΡΡ Π·Π½Π°ΡΠ΅Π½ΠΈΡ Β«Π΄ΠΎΒ» ΠΈ Β«ΠΏΠΎΡΠ»Π΅Β», Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ Π²Π°ΡΠΈΡ
ΡΡΠ΅Π±ΠΎΠ²Π°Π½ΠΈΠΉ.
ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, ΡΠΎΠ·Π΄Π°Π΄ΠΈΠΌ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ IAuditable Ρ Π΄Π°ΡΠ°ΠΌΠΈ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΠΎΠ±ΡΠ΅ΠΊΡΠ°:
public interface IAuditable
{
DateTime Created { get; }
DateTime? Modified { get; }
}
ΠΠΎΠ±Π°Π²ΠΈΠΌ UpdateInterceptor Π΄Π»Ρ Π·Π°ΠΏΠΈΡΠΈ Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ Π°ΡΠ΄ΠΈΡΠ°. ΠΠ½ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ ChangeTracker Π΄Π»Ρ ΠΏΠΎΠΈΡΠΊΠ° Π²ΡΠ΅Ρ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠΎΠ² IAuditable ΠΈ ΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅Ρ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠ΅Π΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΡΠ²ΠΎΠΉΡΡΠ²Π°. ΠΠ΄Π΅ΡΡ ΠΌΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ ΠΌΠ΅ΡΠΎΠ΄ SavingChangesAsync, ΠΊΠΎΡΠΎΡΡΠΉ Π·Π°ΠΏΡΡΠΊΠ°Π΅ΡΡΡ Π΄ΠΎ ΡΠΎΠ³ΠΎ, ΠΊΠ°ΠΊ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π±ΡΠ΄ΡΡ ΡΠΎΡ ΡΠ°Π½Π΅Π½Ρ Π² ΠΠ.
internal sealed class UpdateInterceptor
: SaveChangesInterceptor
{
public override
ValueTask<InterceptionResult<int>>
SavingChangesAsync(
DbContextEventData e,
InterceptionResult<int> result,
CancellationToken ct = default)
{
if (e.Context is not null)
UpdateEntities(e.Context);
return base
.SavingChangesAsync(e, result, ct);
}
private static void
UpdateEntities(DbContext ctx)
{
var now = DateTime.UtcNow;
var entities = ctx
.ChangeTracker
.Entries<IAuditable>()
.ToList();
foreach (var e in entities)
{
if (e.State == EntityState.Added)
e.Property(
nameof(IAuditable.Created)) = now;
if (e.State == EntityState.Modified)
e.Property(
nameof(IAuditable.Modified)) = now;
}
}
}
ΠΡΡ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΌΠΎΠΆΠ½ΠΎ Π»Π΅Π³ΠΊΠΎ ΡΠ°ΡΡΠΈΡΠΈΡΡ, Π²ΠΊΠ»ΡΡΠΈΠ² Π² Π½Π΅Ρ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ ΡΠ΅ΠΊΡΡΠ΅ΠΌ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Π΅.
ΠΠ°ΡΠ΅Π³ΠΈΡΡΡΠΈΡΠΎΠ²Π°ΡΡ ΠΏΠ΅ΡΠ΅Ρ Π²Π°ΡΡΠΈΠΊ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ:
services.AddSingleton<UpdateInterceptor>();
services.AddDbContext<
IApplicationDbContext,
AppDbContext>(
(sp, opts) => opts
.UseSqlServer(connString)
.AddInterceptors(
sp.GetRequiredService<UpdateInterceptor>());
π ΠΠΎΠ΄ΡΠΎΠ±Π½Π΅Π΅
@csharp_1001_notes