将 EF Core InMemory 数据库序列化为 JSON

Serialize EF Core InMemory Database to JSON

我期待在 DbContext 中提取大量实体的增量更改,并将实际的数据库提交委托给后台进程,例如 Azure 网络作业。

尝试过,但无法序列化。

        var deltaJson = "";
        try
        {
            var modifiedEntries = _ctx.ChangeTracker
                .Entries()
                .Select(x => x.Entity)
                .ToList();
            deltaJson = JsonConvert.SerializeObject(modifiedEntries, Formatting.Indented);
        }

My next hope is to use in memory Database and possibly if we could serialize entire object graph of DbContext.

可行吗?您的专家建议和这方面的任何指示都会非常有帮助。

编辑: 我自己的版本:

public class DeltaTracking
{
    public static List<T> Build<T>(ICollection<T> db, ICollection<T> req) where T : IR
    {
        return Build(db, req, new DT<T>());
    }

    public static List<T> Build<T>(ICollection<T> db, ICollection<T> req, IEqualityComparer<T> comp) where T : IStateTracking
    {
        db = db ?? new List<T>();
        req = req ?? new List<T>();

        List<T> added = req.Except(db, comp).ToList();
        foreach (T a in added)
        {
            a.State = TrackingState.Added.ToString();
        }

        List<T> removed = db.Except(req, comp).ToList();
        foreach (T r in removed)
        {
            r.State = TrackingState.Deleted.ToString();
        }

        List<T> unchanged = db.Intersect(req, comp).ToList();
        foreach (T u in unchanged)
        {
            u.State = TrackingState.Unchanged.ToString();
        }
        List<T> resp = added.Union(removed, comp).Union(unchanged, comp).ToList();
        return resp;
    }
}

您可以从更改跟踪器序列化实体,但是鉴于这听起来像是您想要序列化和卸载大量更改,您可能需要将更改打包到较小的页面中,通过网络传输它们,跟踪并在另一侧组成变更集,并确保按顺序重新组合它们,因为变更集将跨越相关实体。

const int pageSize = 1000;
var count = context.ChangeTracker.Entries()
    .Count(x => x.State == EntityState.Modified || x.State == EntityState.Added || x.State == EntityState.Deleted);

var pages = (int) Math.Ceiling((double) count / PageSize);
int loopCounter = 0;
while (loopCounter < pages)
{
   var changes = context.ChangeTracker.Entries()
      .Where(x => x.State == EntityState.Modified || x.State == EntityState.Added || x.State == EntityState.Deleted)
      .Select(x => new { Type = x.Entity.GetType(), State = x.State.ToString(), Original = x.OriginalValues.ToObject(), Updated = x.CurrentValues.ToObject() })
      .Skip(loopCounter * pageSize)
      .Take(pageSize);

   var data = new
   {
      PageNumber = loopCounter,
      Changes = changes,
   };

   string changeData = JsonConvert.SerializeObject(data, Formatting.Indented);
   // Fire across to destination to be processed. Be sure to send the total # of pages because the server cannot start processing these unless they are in order.

.Changes 将包含类型、状态(采取什么操作、更新、添加、删除)以及适用的原始和更新实体值。

如果这只是处理批量更新,则排除 Add/Delete 项并让它们流过。

在接收端,我认为它不会像获取更改的实体并将它们附加到 DbContext 以持久保存更新那样简单。这些可能需要由目标 DbContext 加载并根据附加实体中的字段进行更新。提取用于删除操作的 PK 之类的东西需要做一些工作。添加应该非常简单。

另一个真正的测试是在进行更改的时间与后台服务 运行 之间的并发性。您应该考虑检查任何更新的版本控制。这可能不是一个完整的解决方案,但希望它能提供一些想法或引发一些讨论。

总是可以边走边做一些消息(MSMQ、RabbitMQ)。您的进程一次将一个 "transaction" 发送到队列,而侦听器一次处理一个,完全异步。可以从接收方产生多个进程以获得更好的吞吐量。

更新:消息队列还处理更新序列,同时保持异步。