Asp.net web api Breeze SaveBundleToSaveMap示例中未初始化的JsonSerializer
我试图使用下面链接的SaveBundleToSaveMap片段在BreezeWebAPI实现的服务器端实现自定义保存处理 此示例不按原样工作?(见下文);它们是一个空引用异常,需要注意 SaveWorksState(provider,EntityArray)构造函数调用ContextProvider.CreateEntityInfoFromJson(…)方法,然后该方法调用(类作用域)JsonSerializer.Deserialize(新的JTokenReader(jo),entityType)方法 问题是JsonSerializer未初始化,我们得到了一个空引用。 例如,我添加了这个测试hack来运行代码:Asp.net web api Breeze SaveBundleToSaveMap示例中未初始化的JsonSerializer,asp.net-web-api,breeze,Asp.net Web Api,Breeze,我试图使用下面链接的SaveBundleToSaveMap片段在BreezeWebAPI实现的服务器端实现自定义保存处理 此示例不按原样工作?(见下文);它们是一个空引用异常,需要注意 SaveWorksState(provider,EntityArray)构造函数调用ContextProvider.CreateEntityInfoFromJson(…)方法,然后该方法调用(类作用域)JsonSerializer.Deserialize(新的JTokenReader(jo),entityTyp
protected internal EntityInfo CreateEntityInfoFromJson(dynamic jo, Type entityType) {
//temp fix to init JsonSerializer if SaveChanges has NOT been called
if(JsonSerializer==null) JsonSerializer = CreateJsonSerializer();
var entityInfo = CreateEntityInfo();
entityInfo.Entity = JsonSerializer.Deserialize(new JTokenReader(jo), entityType);
entityInfo.EntityState = (EntityState)Enum.Parse(typeof(EntityState), (String)jo.entityAspect.entityState);
entityInfo.ContextProvider = this;
这个问题不会出现在标准版本位中,因为CreateEntityInfoFromJson总是这样?从SaveChanges()调用下游调用,这意味着JsonSerializer将被初始化
但是,如果将初始化的JsonSerializer作为参数传递给CreateEntityInfoFromJson,以避免将来可能出现的空引用问题,那么结构会更好吗
或者,有没有办法让SaveBundleToSaveMap片段初始化JsonSerializer?它有一个私人setter:(
更新
实现了一个非常粗糙的权宜之计解决方案。如果iDeaBrade的任何人都在观看,最好有一个公共API来转换json saveBundle saveMap
/// <summary>
/// Convert a json saveBundle into a breeze SaveMap
/// </summary>`enter code here`
public static Dictionary<Type, List<EntityInfo>> SaveBundleToSaveMap(JObject saveBundle)
{
var _dynSaveBundle = (dynamic)saveBundle;
var _entitiesArray = (JArray)_dynSaveBundle.entities;
var _provider = new BreezeAdapter();
//Hack 1: Breeze.ContextProvider initializes a global JsonSerializer in its SaveChanges() method
//We are bypassing SaveChanges() and bootstrapping directly into SaveWorkState logic to generate our saveMap
//as such we need to init a serializer here and slipsteam it in via reflection (its got a private setter)
var _serializerSettings = BreezeConfig.Instance.GetJsonSerializerSettings();
var _bootstrappedJsonSerializer = JsonSerializer.Create(_serializerSettings);
//Hack 2:
//How to write to a private setter via reflection
//http://stackoverflow.com/questions/3529270/how-can-a-private-member-accessable-in-derived-class-in-c
PropertyInfo _jsonSerializerProperty = _provider.GetType().GetProperty("JsonSerializer", BindingFlags.Instance | BindingFlags.NonPublic);
//Hack 3: JsonSerializer property is on Breeze.ContextProvider type; not our derived EFContextProvider type so...
_jsonSerializerProperty = _jsonSerializerProperty.DeclaringType.GetProperty("JsonSerializer", BindingFlags.Instance | BindingFlags.NonPublic);
//Finally, we can init the JsonSerializer
_jsonSerializerProperty.SetValue(_provider, _bootstrappedJsonSerializer);
//saveWorkState constructor loads json entitiesArray into saveWorkState.EntityInfoGroups struct
var _saveWorkState = new SaveWorkState(_provider, _entitiesArray);
//BeforeSave logic loads saveWorkState.EntityInfoGroups metadata into saveWorkState.SaveMap
_saveWorkState.BeforeSave();
var _saveMap = _saveWorkState.SaveMap;
return _saveMap;
}
//
///将json保存包转换为breeze保存映射
///”“在这里输入代码`
公共静态字典saveBundle保存映射(JObject saveBundle)
{
var _dynSaveBundle=(动态)saveBundle;
var _entitiesArray=(JArray)_dynSaveBundle.entities;
var_provider=new BreezeAdapter();
//Hack 1:Breeze.ContextProvider在其SaveChanges()方法中初始化全局JsonSerializer
//我们绕过SaveChanges()并直接引导到SaveWorkState逻辑来生成saveMap
//因此,我们需要在这里初始化一个序列化程序,并通过反射(它有一个私有setter)将其导入
var_serializerSettings=BreezeConfig.Instance.GetJsonSerializerSettings();
var _bootstrappedJsonSerializer=JsonSerializer.Create(_serializerSettings);
//黑客2:
//如何通过反射向私有setter写入
//http://stackoverflow.com/questions/3529270/how-can-a-private-member-accessable-in-derived-class-in-c
PropertyInfo _jsonSerializerProperty=_provider.GetType().GetProperty(“JsonSerializer”,BindingFlags.Instance | BindingFlags.NonPublic);
//Hack 3:JsonSerializer属性位于Breeze.ContextProvider类型上;不是派生的EFContextProvider类型,因此。。。
_jsonSerializerProperty=_jsonSerializerProperty.DeclaringType.GetProperty(“JsonSerializer”,BindingFlags.Instance | BindingFlags.NonPublic”);
//最后,我们可以初始化JsonSerializer
_SetValue(_provider,_bootstrappedJsonSerializer);
//saveWorkState构造函数将json entitiesArray加载到saveWorkState.EntityInfoGroups结构中
var _saveWorkState=新的saveWorkState(_provider,_entitiesArray);
//BeforeSave逻辑将saveWorkState.EntityInfoGroups元数据加载到saveWorkState.SaveMap中
_saveWorkState.BeforeSave();
var\u saveMap=\u saveWorkState.saveMap;
返回_saveMap;
}
我对此进行了研究。实际上,您不需要对Breeze代码进行更改即可完成所需操作。ContextProvider
的设计使您可以在保存过程中执行任何您想要的操作
我很好奇:您想执行什么“自定义保存处理”,而今天使用BeforeSave和AfterSave逻辑无法执行?我在您的“权宜之计”代码中看到,您正在调用SaveWorkState
上的BeforeSave
。您还需要什么
作为练习,我写了一篇文章,它能满足你的需求。下面是它的运行方式:
/// <summary>
/// A context whose SaveChanges method does not save
/// but it will prepare its <see cref="SaveWorkState"/> (with SaveMap)
/// so developers can do what they please with the same information.
/// See the <see cref="GetSaveMapFromSaveBundle"/> method;
/// </summary>
public class NorthwindIBDoNotSaveContext : EFContextProvider<NorthwindIBContext_CF>
{
/// <summary>
/// Open whatever is the "connection" to the "database" where you store entity data.
/// This implementation does nothing.
/// </summary>
protected override void OpenDbConnection(){}
/// <summary>
/// Perform your custom save to wherever you store entity data.
/// This implementation does nothing.
/// </summary>
protected override void SaveChangesCore(SaveWorkState saveWorkState) {}
/// <summary>
/// Return the SaveMap that Breeze prepares
/// while performing <see cref="ContextProvider.SaveChanges"/>.
/// </summary>
/// <remarks>
/// Calls SaveChanges which internally creates a <see cref="SaveWorkState"/>
/// from the <see param="saveBundle"/> and then runs the BeforeSave and AfterSave logic (if any).
/// <para>
/// While this works, it is hacky if all you want is the SaveMap.
/// The real purpose of this context is to demonstrate how to
/// pare down a ContextProvider, benefit from the breeze save pre/post processing,
/// and then do your own save inside the <see cref="SaveChangesCore"/>.
/// </para>
/// </remarks>
/// <returns>
/// Returns the <see cref="SaveWorkState.SaveMap"/>.
/// </returns>
public Dictionary<Type, List<EntityInfo>> GetSaveMapFromSaveBundle(JObject saveBundle)
{
SaveChanges(saveBundle); // creates the SaveWorkState and SaveMap as a side-effect
return SaveWorkState.SaveMap;
}
}
是的,它是“黑”的,特别是如果你只想要SaveMap
。但是为什么你只想要SaveMap
?
我们设计了ContextProvider
(及其所有子类),这样您就可以自由支配SaveChangesCore
方法。您可以覆盖该方法,进一步操作SaveMap
,然后委托给基本实现,或者执行任何您想保存实体数据的操作
但是,虽然我不知道您想要的是什么,但将SaveChanges
初始化逻辑提取到它自己的方法中并不难
因此,在下一个版本(1.5.2之后)中,您应该在ContextProvider
中找到以下新方法:
protected void InitializeSaveState(JObject saveBundle)
{
JsonSerializer = CreateJsonSerializer();
var dynSaveBundle = (dynamic)saveBundle;
var entitiesArray = (JArray)dynSaveBundle.entities;
var dynSaveOptions = dynSaveBundle.saveOptions;
SaveOptions = (SaveOptions)JsonSerializer.Deserialize(new JTokenReader(dynSaveOptions), typeof(SaveOptions));
SaveWorkState = new SaveWorkState(this, entitiesArray);
}
SaveChanges
现在调用该方法,然后以以前的方式继续:
public SaveResult SaveChanges(JObject saveBundle, TransactionSettings transactionSettings = null) {
if (SaveWorkState == null || SaveWorkState.WasUsed) {
InitializeSaveState(saveBundle);
}
transactionSettings = transactionSettings ?? BreezeConfig.Instance.GetTransactionSettings();
...
}
请注意,SaveChanges
不会调用InitializeSaveState
两次,如果您已经准备好了SaveWorkState
,比如从外部调用InitializeSaveState
,然后立即调用SaveChanges
。它也不会使用“已使用的”SaveWorkState
保存两次
如果你感兴趣的话,消息来源就在现在
通过将此方法添加到ContextProvider
的子类中,您可以从保存包中获取SaveMap
,如本例所示:
public class NorthwindContextProvider: EFContextProvider<NorthwindIBContext_CF> {
...
public Dictionary<Type, List<EntityInfo>> GetSaveMapFromSaveBundle(JObject saveBundle) {
InitializeSaveState(saveBundle); // Sets initial EntityInfos
SaveWorkState.BeforeSave(); // Creates the SaveMap as byproduct of BeforeSave logic
return SaveWorkState.SaveMap;
}
...
}
你知道了一些事情。我记得我自己也想在这里进行一些分离。我会再联系你的。谢谢沃德!你的解决方案非常有效。我对该功能的使用案例是,我有一个使用breeze控制器的现有系统(并使用JObject保存包公开一个操作)。我想截获此操作的有效负载,并在任何ContextProvider被命中之前接收JObject所关联的实体。我已经获取了您的NorthwindIBDoNotSaveContext示例,并将其包装在一个静态转换类中,现在我可以获得SaveResult和我想要的EntityInfo.entity。太棒了!一个建议n、 您提到您创建了一个静态包装器类。您有吗
public class NorthwindContextProvider: EFContextProvider<NorthwindIBContext_CF> {
...
public Dictionary<Type, List<EntityInfo>> GetSaveMapFromSaveBundle(JObject saveBundle) {
InitializeSaveState(saveBundle); // Sets initial EntityInfos
SaveWorkState.BeforeSave(); // Creates the SaveMap as byproduct of BeforeSave logic
return SaveWorkState.SaveMap;
}
...
}
var saveMap = ContextProvider.GetSaveMapFromSaveBundle(saveBundle);