将 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" 发送到队列,而侦听器一次处理一个,完全异步。可以从接收方产生多个进程以获得更好的吞吐量。
更新:消息队列还处理更新序列,同时保持异步。
我期待在 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" 发送到队列,而侦听器一次处理一个,完全异步。可以从接收方产生多个进程以获得更好的吞吐量。
更新:消息队列还处理更新序列,同时保持异步。