这是 JSON.NET 或实体框架中的错误,还是我在尝试使用 JSON.NET 序列化异常列表时做错了什么? [英] Is this a bug in JSON.NET or Entity Framework or am I doing something wrong while trying to serialize a list of Exceptions with JSON.NET?

查看:29
本文介绍了这是 JSON.NET 或实体框架中的错误,还是我在尝试使用 JSON.NET 序列化异常列表时做错了什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

尝试序列化一组错误时出现此错误:

Got this error when trying to serialize a set of errors:

ISerializable 类型 'System.Data.Entity.Infrastructure.DbUpdateConcurrencyException' 没有有效的构造函数.要正确实现 ISerializable,应该存在采用 SerializationInfo 和 StreamingContext 参数的构造函数."

"ISerializable type 'System.Data.Entity.Infrastructure.DbUpdateConcurrencyException' does not have a valid constructor. To correctly implement ISerializable a constructor that takes SerializationInfo and StreamingContext parameters should be present."

构造函数实际上存在于基类中,但它是一个protected成员.

The constructor is in fact present in the base classes, but it is a protected member.

有人要求查看 JSON:

Someone asked to see the JSON:

{
    "$type": "System.Data.Entity.Infrastructure.DbUpdateConcurrencyException, EntityFramework",
    "ClassName": "System.Data.Entity.Infrastructure.DbUpdateConcurrencyException",
    "Message": "Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.",
    "Data": {
        "$type": "System.Collections.ListDictionaryInternal, mscorlib"
    },
    "InnerException": {
        "$type": "System.Data.Entity.Core.OptimisticConcurrencyException, EntityFramework",
        "ClassName": "System.Data.Entity.Core.OptimisticConcurrencyException",
        "Message": "Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.",
        "Data": {
            "$type": "System.Collections.ListDictionaryInternal, mscorlib"
        },
        "InnerException": null,
        "HelpURL": null,
        "StackTraceString": "   at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.ValidateRowsAffected(Int64 rowsAffected, UpdateCommand source)
   at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
   at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.<Update>b__2(UpdateTranslator ut)
   at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update[T](T noChangesResult, Func`2 updateFunction)
   at System.Data.Entity.Core.EntityClient.Internal.EntityAdapter.Update()
   at System.Data.Entity.Core.Objects.ObjectContext.<SaveChangesToStore>b__35()
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions options, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction)
   at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass2a.<SaveChangesInternal>b__27()
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChanges(SaveOptions options)
   at System.Data.Entity.Internal.InternalContext.SaveChanges()",
        "RemoteStackTraceString": null,
        "RemoteStackIndex": 0,
        "ExceptionMethod": "8
ValidateRowsAffected
EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator
Void ValidateRowsAffected(Int64, System.Data.Entity.Core.Mapping.Update.Internal.UpdateCommand)",
        "HResult": -2146233087,
        "Source": "EntityFramework",
        "WatsonBuckets": null
    },
    "HelpURL": null,
    "StackTraceString": "   at System.Data.Entity.Internal.InternalContext.SaveChanges()
   at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
   at System.Data.Entity.DbContext.SaveChanges()
   at REDACTED FOR DISPLAY ON STACKOVERFLOW",
    "RemoteStackTraceString": null,
    "RemoteStackIndex": 0,
    "ExceptionMethod": "8
SaveChanges
EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Data.Entity.Internal.InternalContext
Int32 SaveChanges()",
    "HResult": -2146233087,
    "Source": "EntityFramework",
    "WatsonBuckets": null,
    "SafeSerializationManager": {
        "$type": "System.Runtime.Serialization.SafeSerializationManager, mscorlib",
        "m_serializedStates": {
            "$type": "System.Collections.Generic.List`1[[System.Object, mscorlib]], mscorlib",
            "$values": [
                {
                    "$type": "System.Data.Entity.Infrastructure.DbUpdateException+DbUpdateExceptionState, EntityFramework",
                    "InvolvesIndependentAssociations": false
                }
            ]
        }
    },
    "CLR_SafeSerializationManager_RealType": "System.Data.Entity.Infrastructure.DbUpdateConcurrencyException, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
}

以下是引发异常的示例代码:

Here is example code that throws the exception:

var serializationSettings = new JsonSerializerSettings() {
    DateFormatHandling = DateFormatHandling.IsoDateFormat,
    DateParseHandling = DateParseHandling.DateTime,
    DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind,
    DefaultValueHandling = DefaultValueHandling.Include,
    TypeNameHandling = TypeNameHandling.All,
    TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
    ObjectCreationHandling = ObjectCreationHandling.Replace, //Necessary for subclassing list types
    ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};

var json = JsonConvert.SerializeObject( new System.Data.Entity.Infrastructure.DbUpdateConcurrencyException( "hi" ), serializationSettings );
if (json == null)
    return null;
var err = JsonConvert.DeserializeObject<System.Data.Entity.Infrastructure.DbUpdateConcurrencyException>( json, serializationSettings ); //throws error

这变得更加奇怪,因为正如一个答案所指出的那样,这是一个特殊的类,因为它不直接实现具有预期签名的构造函数.相反,对类进行反编译显示了某种非常直接的理由",因为它没有实现预期的构造函数......

This gets even stranger, because as one answer points out, this is an exceptional class in that it does not directly implement a constructor with the expected signature. Instead, decompiling the class shows some kind of, quite literal, "justification" for NOT implementing the expected constructor...

/// <summary>
/// Exception thrown by <see cref="T:System.Data.Entity.DbContext" /> when the saving of changes to the database fails.
/// Note that state entries referenced by this exception are not serialized due to security and accesses to the
/// state entries after serialization will return null.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors",
    Justification = "SerializeObjectState used instead")]
[Serializable]
public class DbUpdateException : DataException
{
    /// <summary>
    /// Holds exception state that will be serialized when the exception is serialized.
    /// </summary>
    [Serializable]
    private struct DbUpdateExceptionState : ISafeSerializationData
    {

推荐答案

根据Json.net 文档,

According to the Json.net documentation,

ISerializable

实现 ISerializable 的类型被序列化为 JSON 对象.序列化时,仅使用从 ISerializable.GetObjectData 返回的值;类型上的成员被忽略.反序列化时,调用带有 SerializationInfo 和 StreamingContext 的构造函数,传递 JSON 对象的值.

Types that implement ISerializable are serialized as JSON objects. When serializing, only the values returned from ISerializable.GetObjectData are used; members on the type are ignored. When deserializing, the constructor with a SerializationInfo and StreamingContext is called, passing the JSON object's values.

在不需要这种行为的情况下,可以将 JsonObjectAttribute 放置在实现 ISerializable 的 .NET 类型上,以强制将其序列化为普通 JSON 对象.

In situations where this behavior is not wanted, the JsonObjectAttribute can be placed on a .NET type that implements ISerializable to force it to be serialized as a normal JSON object.

由于您不欠 DbUpdateConcurrencyException 类,解决方法可能是创建一个从 DbUpdateConcurrencyException 派生的自定义异常类并用属性 标记它JsonObject.

Since you don't owe the DbUpdateConcurrencyException class, A workaround could be to create a custom exception class which derive from the DbUpdateConcurrencyException and Mark it with attribute JsonObject.

    [JsonObject]
    class CustomException : DbUpdateConcurrencyException
    {
        public CustomException(string message) : base(message) { }
    }

     // Serialize the new customException
     var json = JsonConvert.SerializeObject(
         new CustomException("hi"), serializationSettings);

     //shall not throw error now
     DbUpdateConcurrencyException err = 
          JsonConvert.DeserializeObject<DbUpdateConcurrencyException>(json, serializationSettings); 

这只是一个 POC,我试图使它适用于 JSON.Net.为所有继承 ISerializable 且不需要构造函数的类型创建自定义类是没有意义的.也许您可以尝试创建 Castle 核心 DynamicProxy 生成器来包装抛出的异常,这些异常是 ISerializable 并使用 JsonObject 属性即时标记它们在序列化它们之前.

This is a just a POC that I tried to make it work for JSON.Net. It makes no sense to create custom classes for all type which inherits ISerializable and doesn't have required constructor. May be you can try creating Castle core DynamicProxy generator to wrap the thrown exception which are ISerializable and mark them with JsonObject attribute on-the-fly before serializing them.

你是对的.Json.net 无法找到受保护的构造函数,因为继承就像

And you're right. Json.net is not able to find the protected constructor because inheritance goes like

DbUpdateConcurrencyException <- DbUpdateException <- DataException 

还有 DataException 类具有 json.net 正在寻找的 Protected 构造函数..Net 框架中从 SystemException 派生的每个异常类都有这个构造函数作为受保护的构造函数,但 DbUpdateException &&DbUpdateConcurrencyException 没有它.所以你现在可以猜到应该怪谁了(IMO EF).

And DataException class have the Protected constructor which json.net is looking for. Every exception class in .Net framework which is derived from SystemException have this constructor as protected constructor but DbUpdateException && DbUpdateConcurrencyException doesn't have it. So you can guess who to blame now (IMO EF).

以下是我发现的类,它们缺少标准的可序列化构造函数,并且在反序列化期间会抛出异常.

Following are the classes I found which have the standard serializable constructor missing and would throw exception during deserialization.

  • EntitySqlException
  • PropertyConstraintException
  • DbUpdateConcurrencyException
  • DbUpdateException
  • 工具异常
  • DbEntityValidationException
  • 命令行异常

我将此问题写给 EF 团队.

这篇关于这是 JSON.NET 或实体框架中的错误,还是我在尝试使用 JSON.NET 序列化异常列表时做错了什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆