本文介绍了未初始化JsonSerializer微风SaveBundleToSaveMap样本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在尝试使用下面SaveBundleToSaveMap挂片断来实现自定义处理保存在微风的网络API实现的服务器端。

<一个href=\"https://github.com/IdeaBlade/Breeze/blame/3ee0424851e8bae87ba8b7a48aee645a2378d0a6/Samples/Doc$c$c/Doc$c$c.DataAccess.EF/SaveBundleToSaveMap.cs#\"相对=nofollow> SaveBundleToSaveMap

此示例不作为是功? (见下文);他们是可以使用一些注意一个空引用异常。

该SaveWorkState(供应商,entitiesArray)构造函数调用ContextProvider.CreateEntityInfoFromJson(...)方法,然后调用(类范围)JsonSerializer.Deserialize(新JTokenReader(JO)的EntityType)方法。

问题是,JsonSerializer是未初始化的,我们得到一个空引用exeption。
对于如我说这个测试黑客以获得$ ​​C $ C运行:

 受保护的内部EntityInfo CreateEntityInfoFromJson(动态乔类型的EntityType){
      //临时修复程序初始化JsonSerializer的SaveChanges如果尚未调用
      如果(JsonSerializer == NULL)JsonSerializer = CreateJsonSerializer();      变种entityInfo = CreateEntityInfo();      entityInfo.Entity = JsonSerializer.Deserialize(新JTokenReader(JO)的EntityType);
      entityInfo.EntityState =(EntityState)Enum.Parse(typeof运算(EntityState),(串)jo.entityAspect.entityState);
      entityInfo.ContextProvider =这一点;

作为CreateEntityInfoFromJson始终是此问题不会在标准版本比特发生的?从调用SaveChanges()调用,这意味着JsonSerializer被初始化下游调用。

但是,如果初始化JsonSerializer传递给CreateEntityInfoFromJson作为参数,以避免未来潜在的空引用问题的事情会更好的结构?

另外,有没有办法让SaveBundleToSaveMap片段给init JsonSerializer?它有一个私人二传手:(

更新

实施了非常哈克权宜之计。如果有人在IdeaBlade是看,将是巨大的,有一个公共的API转换为从JSON saveBundle&LT; - > saveMap。

  ///&LT;总结&gt;
///转换一个JSON saveBundle成微风SaveMap
///&LT; /总结&gt;进入`code here`
公共静态字典&LT;类型,列表与LT; EntityInfo&GT;&GT; SaveBundleToSaveMap(JObject saveBundle)
{
   VAR _dynSaveBundle =(动态)saveBundle;
   VAR _entitiesArray =(JArray)_dynSaveBundle.entities;
   VAR _provider =新BreezeAdapter();   //哈克1:Breeze.ContextProvider在它的SaveChanges初始化的全局JsonSerializer()方法
   //我们的SaveChanges旁路()和引导直接进入SaveWorkState逻辑来生成我们saveMap
   //因此我们需要在这里初始化一个串行器及通过反射slipsteam它(它有一个私人的setter)
   变种_serializerSettings = BreezeConfig.Instance.GetJsonSerializerSettings();
   VAR _bootstrappedJsonSerializer = JsonSerializer.Create(_serializerSettings);   //哈克2:
   //如何写通过反射私人二传手
   //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可);
   //哈克3:JsonSerializer属性是基于Breeze.ContextProvider类型;不是我们的派生型EFContextProvider所以...
   _jsonSerializerProperty = _jsonSerializerProperty.DeclaringType.GetProperty(JsonSerializer,BindingFlags.Instance | BindingFlags.NonPublic可);
   //最后,我们可以初始化的JsonSerializer
   _jsonSerializerProperty.SetValue(_provider,_bootstrappedJsonSerializer);   // saveWorkState构造函数加载JSON entitiesArray到saveWorkState.EntityInfoGroups结构
   VAR _saveWorkState =新SaveWorkState(_provider,_entitiesArray);
   // BeforeSave逻辑将saveWorkState.EntityInfoGroups元到saveWorkState.SaveMap
   _saveWorkState.BeforeSave();
   VAR _saveMap = _saveWorkState.SaveMap;   返回_saveMap;
}


解决方案

我看着这一点。你实际上并不需要做出改变的微风code来完成你想要的东西。在 ContextProvider 是这样设计的,你可以做几乎任何你在保存。希望

我很好奇:什么是自定义的保存处理你要执行,你不能今天做的BeforeSave和AfterSave逻辑是什么?我看到你的权宜之计code你调用 BeforeSave SaveWorkState 。你还需要什么呢?

作为练习,我写了一个 NorthwindIBDoNotSaveContext ,你想要做什么。下面是它如何去:

  ///&LT;总结&gt;
///上下文的SaveChanges方法不保存
///但它将prepare其&LT;见CREF =SaveWorkState/&GT; (带SaveMap)
///因此开发人员可以做他们请什么相同的信息。
///见的&lt;见CREF =GetSaveMapFromSaveBundle/&GT;方法;
///&LT; /总结&gt;
公共类NorthwindIBDoNotSaveContext:EFContextProvider&LT; NorthwindIBContext_CF&GT;
{
  ///&LT;总结&gt;
  ///打开无论是连接到您存储实体数据的数据库。
  ///此实现什么都不做。
  ///&LT; /总结&gt;
  保护覆盖无效OpenDbConnection(){}  ///&LT;总结&gt;
  ///执行您的自定义的保存,无论你存储实体数据。
  ///此实现什么都不做。
  ///&LT; /总结&gt;
  保护覆盖无效SaveChangesCore(SaveWorkState saveWorkState){}  ///&LT;总结&gt;
  ///返回SaveMap的微风prepares
  ///在执行&LT;见CREF =ContextProvider.SaveChanges/取代。
  ///&LT; /总结&gt;
  ///&LT;&言论GT;
  ///调用其内部创建一个&LT的SaveChanges;见CREF =SaveWorkState/&GT;
  ///从在&lt;见参数=saveBundle/&GT;然后运行BeforeSave和AfterSave逻辑(如有的话)。
  ///&LT;&款GT;
  ///虽然这个工作,它是哈克如果你想要的是SaveMap。
  ///此背景下的真正目的是演示如何
  ///从微风削减下来ContextProvider,效益节省pre /后处理,
  ///然后做自己保存的&lt里面;见CREF =SaveChangesCore/取代。
  ///&LT; /对&GT;
  ///&LT; /言论&GT;
  ///&LT;退货和GT;
  ///返回的&lt;见CREF =SaveWorkState.SaveMap/取代。
  ///&LT; /回报&GT;
  公共字典&LT;类型,列表与LT; EntityInfo&GT;&GT; GetSaveMapFromSaveBundle(JObject saveBundle)
  {
    调用SaveChanges(saveBundle); //创建SaveWorkState和SaveMap作为一个副作用
    返回SaveWorkState.SaveMap;
  }
}

和这里是你如何使用它来获取 SaveMap

  VAR saveMap =新NorthwindIBDoNotSaveContext()GetSaveMapFromSaveBundle(saveBundle)。

是的,它是哈克,特别是如果你想要的是 SaveMap 。但为什么你只是想 SaveMap

我们已经设计了 ContextProvider (及其所有子类),使得您在 SaveChangesCore 方法。你可以覆盖,进一步操纵的 SaveMap ,然后要么委托给基实现还是你心里有其他任何用于保存实体数据。

不过,虽然我没有看到你以后,它是不是所有的硬解压的SaveChanges 初始化逻辑到它自己的方法。

因此​​在下一版本(1.5.2)后,你会发现在下面的新方法 ContextProvider

 保护无效InitializeSaveState(JObject saveBundle)
{
  JsonSerializer = CreateJsonSerializer();  VAR dynSaveBundle =(动态)saveBundle;
  VAR entitiesArray =(JArray)dynSaveBundle.entities;
  VAR dynSaveOptions = dynSaveBundle.saveOptions;
  SaveOptions =(SaveOptions)JsonSerializer.Deserialize(新JTokenReader(dynSaveOptions)的typeof(SaveOptions));
  SaveWorkState =新SaveWorkState(这一点,entitiesArray);
}

的SaveChanges 现在调用该方法在previous方式继续之前:

 公共SaveResult的SaveChanges(JObject saveBundle,TransactionSettings transactionSettings = NULL){  如果(SaveWorkState == NULL || SaveWorkState.WasUsed){
    InitializeSaveState(saveBundle);
  }  transactionSettings = transactionSettings? BreezeConfig.Instance.GetTransactionSettings();
  ...
}

注意的SaveChanges 不会叫 InitializeSaveState 如果您两次已经prepared的 SaveWorkState ,比方说,叫 InitializeSaveState 外部然后叫的SaveChanges 立即其后。它也不会与一个拿来主义 SaveWorkState 两次保存。

源是签入到GitHub上现在如果你有兴趣。

您将能够获得 SaveMap 从加入这个方法来保存你的包 子类 ContextProvider 在这个例子:

 公共类NorthwindContextProvider:EFContextProvider&LT; NorthwindIBContext_CF&GT; {
  ...
  公共字典&LT;类型,列表与LT; EntityInfo&GT;&GT; GetSaveMapFromSaveBundle(JObject saveBundle){
    InitializeSaveState(saveBundle); //设置初始EntityInfos
    SaveWorkState.BeforeSave(); //创建SaveMap为BeforeSave逻辑的副产品
    返回SaveWorkState.SaveMap;
  }
  ...
}

现在您使用如下:

  VAR saveMap = ContextProvider.GetSaveMapFromSaveBundle(saveBundle);

I'm attempting to use the SaveBundleToSaveMap snippet linked below to implement custom save handling on the server side of a breeze web api implementation.

SaveBundleToSaveMap

This sample does not work as is? (see below); their is a null reference exception which could use some attention.

The SaveWorkState(provider, entitiesArray) constructor calls the ContextProvider.CreateEntityInfoFromJson(...) method which then calls (the class scoped) JsonSerializer.Deserialize(new JTokenReader(jo), entityType) method.

The issue is that JsonSerializer is uninitialised and we get a null reference exeption.For e.g. I added this test hack to get the code running:

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;

This issue does not occur in the standard release bits as CreateEntityInfoFromJson is always? called downstream from a SaveChanges() call which means the JsonSerializer gets initialised.

However, things would be better structured if an initialised JsonSerializer was passed to CreateEntityInfoFromJson as a parameter to avoid potential future null reference issues?

Alternately, is there a way to get the SaveBundleToSaveMap snippet to init the JsonSerializer? Its got a private setter :(

UPDATE

Implemented a very hacky stopgap solution. If anyone at IdeaBlade is watching, would be great to have a public API to convert to and from 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;
}
解决方案

I looked into this. You don't actually need to make a change to the Breeze code to accomplish what you want. The ContextProvider is designed such that you can do just about whatever you want during save.

I'm curious: what "custom save handling" do you want to perform that you can't do today with the BeforeSave and AfterSave logic? I see in your "stopgap" code that you're calling BeforeSave on the SaveWorkState. What more do you need?

As an exercise, I wrote a NorthwindIBDoNotSaveContext that does what you want. Here's how it goes:

/// <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;
  }
}

And here's how you could use it to get the SaveMap:

var saveMap = new NorthwindIBDoNotSaveContext().GetSaveMapFromSaveBundle(saveBundle);

Yes, it is "hacky", particularly if all you want is the SaveMap. But why do you just want the SaveMap?

We've designed the ContextProvider (and all of its sub-classes) such that you have free reign over the SaveChangesCore method. You could override that, further manipulate the SaveMap, then either delegate to the base implementation or do whatever else you have in mind for saving the entity data.

But while I don't see what you're after, it was not all that hard to extract the SaveChanges initialization logic into its own method.

So in the next release (after 1.5.2), you should find the following new method in the 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 now calls that method before continuing on in its previous manner:

public SaveResult SaveChanges(JObject saveBundle, TransactionSettings transactionSettings = null) {

  if (SaveWorkState == null || SaveWorkState.WasUsed) {
    InitializeSaveState(saveBundle);
  }

  transactionSettings = transactionSettings ?? BreezeConfig.Instance.GetTransactionSettings();
  ...
}

Notice that SaveChanges won't call InitializeSaveState twice if you've already prepared the SaveWorkState by, say, calling InitializeSaveState externally and then called SaveChanges immediately thereafter. It also won't save twice with a "used" SaveWorkState.

The source is checked into github right now if you're interested.

You'll be able to get the SaveMap from a save bundle by adding this method to your sub-class of a ContextProvider as in this example:

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;
  }
  ...
}

Now you use that as follows:

  var saveMap =  ContextProvider.GetSaveMapFromSaveBundle(saveBundle);

这篇关于未初始化JsonSerializer微风SaveBundleToSaveMap样本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-26 20:13