breeze - 在保存之前根据实体的导航属性修改服务器上的实体
breeze - modify an entity, on the server, based upon it's navigational properties, before saving
有没有办法在服务器端 breeze 的 BeforeSaveEntity(或保存前的任何其他地方)中获取实体的导航 属性 的 "current" 值?目前,我指的是数据库中存在的内容,合并了任何传入的更改。这不是为了验证,而是我正在计算 parent 属性 的值(我不想要在客户端上)基于 parent 字段和 children 字段...
例如,
public class Parent {
public ICollection<Child> Children{ get; set; }
}
。 . .
protected override bool BeforeSaveEntity(EntityInfo entityInfo) {
if (entityInfo.Entity.GetType() == typeof(Parent) &&
(entityInfo.EntityState == EntityState.Added || entityInfo.EntityState == EntityState.Updated)) {
// Lazy load Parent's Children collection out of breeze's context
// so items are "current' (existing merged with changes)
Parent parent = (Parent)entityInfo.Entity;
Context.Entry(parent).Collection(p => p.Children).Load();
// this throws exception Member 'Load' cannot be called for property
// 'Children' because the entity of type 'Parent' does not exist in the context.
}
}
我认为它们还不在 DBContext 中。我能想到的就是从数据库中检索现有的 children,然后手动合并 BeforeSaveEntities 中的更改,这很麻烦。
延迟加载在 Breeze 用于保存的 DbContext 中未启用。原因详见this SO answer。
您应该在 separate DbContext 中加载任何其他实体。
下面是我在项目中的做法示例。也许 MergeEntities 和 DetachEntities 方法应该包含在 Breeze 中,以便更简单地执行此操作。
protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
{
// create a separate context for computation, so we don't pollute the main saving context
using (var newContext = new MyDbContext(EntityConnection, false))
{
var parentFromClient = (Parent)saveMap[typeof(Parent)][0].Entity;
// Load the necessary data into the newContext
var parentFromDb = newContext.Parents.Where(p => p.ParentId == parentFromClient.ParentId)
.Include("Children").ToList();
// ... load whatever else you need...
// Attach the client entities to the ObjectContext, which merges them and reconnects the navigation properties
var objectContext = ((IObjectContextAdapter)newContext).ObjectContext;
var objectStateEntries = MergeEntities(objectContext, saveMap);
// ... perform your business logic...
// Remove the entities from the second context, so they can be saved in the original context
DetachEntities(objectContext, saveMap);
}
return saveMap;
}
/// Attach the client entities to the ObjectContext, which merges them and reconnects the navigation properties
Dictionary<ObjectStateEntry, EntityInfo> MergeEntities(ObjectContext oc, Dictionary<Type, List<EntityInfo>> saveMap)
{
var oseEntityInfo = new Dictionary<ObjectStateEntry, EntityInfo>();
foreach (var type in saveMap.Keys)
{
var entitySet = this.GetEntitySetName(type);
foreach(var entityInfo in saveMap[type])
{
var entityKey = oc.CreateEntityKey(entitySet, entityInfo.Entity);
ObjectStateEntry ose;
if (oc.ObjectStateManager.TryGetObjectStateEntry(entityKey, out ose))
{
if (ose.State != System.Data.Entity.EntityState.Deleted)
ose.ApplyCurrentValues(entityInfo.Entity);
}
else
{
oc.AttachTo(entitySet, entityInfo.Entity);
ose = oc.ObjectStateManager.GetObjectStateEntry(entityKey);
}
if (entityInfo.EntityState == Breeze.ContextProvider.EntityState.Deleted)
{
ose.Delete();
}
oseEntityInfo.Add(ose, entityInfo);
}
}
return oseEntityInfo;
}
/// Remove the entities in saveMap from the ObjectContext; this separates their navigation properties
static void DetachEntities(ObjectContext oc, Dictionary<Type, List<EntityInfo>> saveMap)
{
foreach (var type in saveMap.Keys)
{
foreach (var entityInfo in saveMap[type])
{
try
{
oc.Detach(entityInfo.Entity);
}
catch
{ // the object cannot be detached because it is not attached
}
}
}
}
有没有办法在服务器端 breeze 的 BeforeSaveEntity(或保存前的任何其他地方)中获取实体的导航 属性 的 "current" 值?目前,我指的是数据库中存在的内容,合并了任何传入的更改。这不是为了验证,而是我正在计算 parent 属性 的值(我不想要在客户端上)基于 parent 字段和 children 字段...
例如,
public class Parent {
public ICollection<Child> Children{ get; set; }
}
。 . .
protected override bool BeforeSaveEntity(EntityInfo entityInfo) {
if (entityInfo.Entity.GetType() == typeof(Parent) &&
(entityInfo.EntityState == EntityState.Added || entityInfo.EntityState == EntityState.Updated)) {
// Lazy load Parent's Children collection out of breeze's context
// so items are "current' (existing merged with changes)
Parent parent = (Parent)entityInfo.Entity;
Context.Entry(parent).Collection(p => p.Children).Load();
// this throws exception Member 'Load' cannot be called for property
// 'Children' because the entity of type 'Parent' does not exist in the context.
}
}
我认为它们还不在 DBContext 中。我能想到的就是从数据库中检索现有的 children,然后手动合并 BeforeSaveEntities 中的更改,这很麻烦。
延迟加载在 Breeze 用于保存的 DbContext 中未启用。原因详见this SO answer。
您应该在 separate DbContext 中加载任何其他实体。
下面是我在项目中的做法示例。也许 MergeEntities 和 DetachEntities 方法应该包含在 Breeze 中,以便更简单地执行此操作。
protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
{
// create a separate context for computation, so we don't pollute the main saving context
using (var newContext = new MyDbContext(EntityConnection, false))
{
var parentFromClient = (Parent)saveMap[typeof(Parent)][0].Entity;
// Load the necessary data into the newContext
var parentFromDb = newContext.Parents.Where(p => p.ParentId == parentFromClient.ParentId)
.Include("Children").ToList();
// ... load whatever else you need...
// Attach the client entities to the ObjectContext, which merges them and reconnects the navigation properties
var objectContext = ((IObjectContextAdapter)newContext).ObjectContext;
var objectStateEntries = MergeEntities(objectContext, saveMap);
// ... perform your business logic...
// Remove the entities from the second context, so they can be saved in the original context
DetachEntities(objectContext, saveMap);
}
return saveMap;
}
/// Attach the client entities to the ObjectContext, which merges them and reconnects the navigation properties
Dictionary<ObjectStateEntry, EntityInfo> MergeEntities(ObjectContext oc, Dictionary<Type, List<EntityInfo>> saveMap)
{
var oseEntityInfo = new Dictionary<ObjectStateEntry, EntityInfo>();
foreach (var type in saveMap.Keys)
{
var entitySet = this.GetEntitySetName(type);
foreach(var entityInfo in saveMap[type])
{
var entityKey = oc.CreateEntityKey(entitySet, entityInfo.Entity);
ObjectStateEntry ose;
if (oc.ObjectStateManager.TryGetObjectStateEntry(entityKey, out ose))
{
if (ose.State != System.Data.Entity.EntityState.Deleted)
ose.ApplyCurrentValues(entityInfo.Entity);
}
else
{
oc.AttachTo(entitySet, entityInfo.Entity);
ose = oc.ObjectStateManager.GetObjectStateEntry(entityKey);
}
if (entityInfo.EntityState == Breeze.ContextProvider.EntityState.Deleted)
{
ose.Delete();
}
oseEntityInfo.Add(ose, entityInfo);
}
}
return oseEntityInfo;
}
/// Remove the entities in saveMap from the ObjectContext; this separates their navigation properties
static void DetachEntities(ObjectContext oc, Dictionary<Type, List<EntityInfo>> saveMap)
{
foreach (var type in saveMap.Keys)
{
foreach (var entityInfo in saveMap[type])
{
try
{
oc.Detach(entityInfo.Entity);
}
catch
{ // the object cannot be detached because it is not attached
}
}
}
}