Entity Framework ChangeTracker Stream и сохранение в запросе впоследствии - PullRequest
0 голосов
/ 06 октября 2019

Я хочу проверить, когда определенная сущность изменена / обновлена ​​/ вставлена ​​/ удалена в Entity Framework (с базой данных). (например, Таблица сущностей ProductType)

** Хотелось бы, чтобы Change Tracker был сохранен и опрошен позже, так как «изменения дорожки теряются, если они не сохраняются до уничтожения объекта DbContext». Для анализа требуется только то, чтобы ChangeTracker оставался в течение пары часов, а при необходимости сохранялся в MessageQueue. MemoryCache также может работать.

1) Кто-то рекомендует использовать LoggerFactory:

optionsBuilder.UseLoggerFactory(loggerFactory) 

https://www.entityframeworktutorial.net/efcore/logging-in-entityframework-core.aspx

Однако может быть трудно разобрать как обновления, модификации, удаленияможет быть сделано с помощью Join, псевдонимов, скобок, с помощью сложных операторов SQL, часто генерируемых EF. Таким образом, синтаксический анализ текста может быть неточным.

INSERT INTO [Students] ([DateOfBirth], [GradeId], [Height], [Photo], [Stud
entName], [Weight])
VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
SELECT [StudentID]
FROM [Students]
WHERE @@ROWCOUNT = 1 AND [StudentID] = scope_identity();
info: Microsoft.EntityFrameworkCore.Database.Command[200101]
Executed DbCommand (68ms) [Parameters=[@p0='' (DbType = DateTime2), @p1=''
(DbType = Int32), @p2='0', @p3='' (Size = 8000) (DbType = Binary), @p4='Steve'
(Size = 4000), @p5='0'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
INSERT INTO [Students] ([DateOfBirth], [GradeId], [Height], [Photo], [Stud
entName], [Weight])
VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
SELECT [StudentID]
FROM [Students]
WHERE @@ROWCOUNT = 1 AND [StudentID] = scope_identity();
A data reader was disposed.
dbug: Microsoft.EntityFrameworkCore.Database.Transaction[2002

2) Вариант 2 - ChangeTracker, другие рекомендовали использовать ChangeTracker перед оператором SaveChanges, поскольку он намного чище (см. Запрос ниже). Однако выполнение этого перед каждым оператором SaveChanges, учитывая, что у нас 500 транзакций в секунду, влияет на скорость работы приложения.

Таким образом, существует ли какой-либо способ для потоковой передачи и сохранения истории ChangeTracker в журнал после сохранения изменений, где можно упростить запроскакие объекты изменились? Оптимально это выяснить после завершения транзакции, без блокировки транзакций.

var Entries = context.ChangeTracker
           .Entries()
           .Where(x => x.State == EntityState.Modified || x.State == EntityState.Deleted|| x.State == EntityState.Added)
           .Select(x =>x.Entity)
           .ToList();

https://entityframework.net/change-tracker

1 Ответ

0 голосов
/ 07 октября 2019

@ DerrikRodgers, время, необходимое для проверки, является ли сущность экземпляром определенного типа, не сопоставимо со временем транзакции. 500 транзакций в секунду будут работать абсолютно нормально с предоставленным вами кодом.

public void ReportChanges()
{
  var entities=ChangeTracker
           .Entries()
           .Where(x => x.Entity is Product)
           .Where(x => x.State == EntityState.Modified || x.State == EntityState.Deleted || x.State == EntityState.Added)
           .GroupBy(x=>x.State, x => x.Entity as Product)
           .ToList();
  ... // Producer/Consumer to do not block current thread and control concurrency
}

Получите базовый уровень производительности с помощью синтетического теста, примерно так:

var cc = new ApplicationDbContext();
for (int i = 0; i < 100; i++)
    cc.Users.Add(new ApplicationUser() { Id = i });

var sw = Stopwatch.StartNew();
for (int i = 0; i < 1000; i++)
    cc.ReportChanges();
sw.Stop();
Console.WriteLine($"1000 times to report 100 entities took {sw.ElapsedMilliseconds}ms. Rate {1000*100/(sw.ElapsedMilliseconds/1000.0)} items/s");
Console.ReadKey();

// 1000 times to report 100 entities took 461ms. Rate 216919.739696312 items/s

Еще один вопрос: что вы собираетесь делать? делать с отфильтрованными объектами. Эта вещь может быть медленной. Например, если вы попытаетесь зарегистрировать их все и не будете использовать асинхронные приемники / цели в своем регистраторе, это будет медленно. В этом случае вы можете реализовать шаблон Producer / Consumer и отфильтровать отфильтрованные сущности другому потребителю, который отправит их в другой поток и сделает что-то длинное. Rx.NET может быть очень полезным в этом случае.

Вместо производителя / потребителя вы можете просто запустить Task, но это может привести к истощению пула потоков, и я бы рекомендовал явно контролировать параллельность ваших «медленных» операций.

...