ScriptIgnore标签被忽略,即使ApplyToOverrides = true..它在LinqPad上也能工作
本文关键字:LinqPad 它在 true 工作 ApplyToOverrides 标签 即使 ScriptIgnore | 更新日期: 2023-09-27 18:09:13
基本上,我试图使用JavaScriptSerializer将一个简单的实体发送到json中。是的,我知道你想让我为它开设一个多余的课程,然后把它塞进AutoMapper,我是在自找麻烦。幽默我。
我使用实体框架6来获取一个简单的对象来获取一个简单的对象。
下面是我的测试代码: [TestMethod]
public void TestEntityTest()
{
var db = new TestDbContext();
var ent = db.ResupplyForms.SingleOrDefault(e => e.Guid == new Guid("55117161-F3FA-4291-8E9B-A67F3B416097"));
var json = new JavaScriptSerializer().Serialize(ent);
}
非常直接。获取对象并序列化。
显示以下错误:
An exception of type 'System.InvalidOperationException' occurred in System.Web.Extensions.dll but was not handled in user code
Additional information: A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.ResupplyForm_13763C1B587B4145B35C75CE2D5394EBED19F93943F42503204F91E0B9B4294D'.
实体:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using System.Data.Entity.Spatial;
using Rome.Model.DataDictionary;
using System.Web.Script.Serialization;
namespace Rome.Model.Form
{
[Table(nameof(ResupplyForm))]
public partial class ResupplyForm
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ResupplyFormID {get;set;}
public Guid Guid {get;set;}
public int? RecordStatus {get;set;}
[ForeignKey(nameof(RecordStatus))]
[ScriptIgnore(ApplyToOverrides = true)]
public virtual LookupItem RecordStatusLookupItem {get;set;}
}
}
我将省略LookupItem的def,因为它会进入我项目的整个其余部分的模式,并且没有一个正常的世界应该重要,因为我已经将其标记为"忽略"。
这里有一个超级简单的上下文:
public class TestDbContext : DbContext
{
public TestDbContext()
: base("data source=.;initial catalog=studybaseline;integrated security=True;pooling=False;multipleactiveresultsets=False")
{
}
public virtual DbSet<ResupplyForm> ResupplyForms { get; set; }
}
现在,惊喜:一个运行完美的LinqPad查询,使用与我的代码片段完全相同的代码:
var db = new Rome.Model.Form.TestDbContext();
var ent = db.ResupplyForms.SingleOrDefault(e => e.Guid == new Guid("55117161-F3FA-4291-8E9B-A67F3B416097"));
var json = new JavaScriptSerializer().Serialize(ent).Dump();
返回
{"ResupplyFormID":1,"Guid":"55117161-f3fa-4291-8e9b-a67f3b416097","RecordStatus":null}
我一整天都在为这件事绞尽脑汁,所以任何帮助都很感激。
好吧,一天后我进一步挖掘了这个问题,并找到了原因:与[ScriptIgnore(ApplyToOverrides = true)]
的事情没有关系。它与实体框架为每个实体创建的EntityProxy子类有关。我的ResupplyForm实际上并没有在我的测试中用作ResupplyForm…相反,它是一个EntityProxy子类。
这个EntityProxy子类增加了一个新成员_entityWrapper。如果EntityProxy包装了一个没有导航属性的类,那么_entityWrapper就不包含任何循环…但是一旦添加了导航属性,_entityWrapper就会包含循环,这会破坏序列化。
模糊的错误信息破坏一切。如果JavaScriptSerializer告诉我哪个字段是坏的,我可以节省很多时间。
无论如何,我应该考虑切换到NewtonSoft,但这产生了自己的问题(另一个帖子),但我创建了一个非常粗糙的解决方案:
public static class JsonSerializerExtensions
{
/// <summary>
/// Convert entity to JSON without blowing up on cyclic reference.
/// </summary>
/// <param name="target">The object to serialize</param>
/// <param name="entityTypes">Any entity-framework-related types that might be involved in this serialization. If null, it will only use the type of "target".</param>
/// <param name="ignoreNulls">Whether nulls should be serialized or not.</param>
/// <returns>Json</returns>
/// <remarks>This requires some explanation: all POCOs used by entites aren't their true form.
/// They're subclassed proxies of the object you *think* you're defining. These add a new member
/// _EntityWrapper, which contains cyclic references that break the javascript serializer.
/// This is Json Serializer function that skips _EntityWrapper for any type in the entityTypes list.
/// If you've a complicated result object that *contains* entities, forward-declare them with entityTypes.
/// If you're just serializing one object, you can omit entityTypes.
///</remarks>
public static string ToJsonString(this object target, IEnumerable<Type> entityTypes = null, bool ignoreNulls = true)
{
var javaScriptSerializer = new JavaScriptSerializer();
if(entityTypes == null)
{
entityTypes = new[] { target.GetType() };
}
javaScriptSerializer.RegisterConverters(new[] { new EntityProxyConverter(entityTypes, ignoreNulls) });
return javaScriptSerializer.Serialize(target);
}
}
public class EntityProxyConverter : JavaScriptConverter
{
IEnumerable<Type> _EntityTypes = null;
bool _IgnoreNulls;
public EntityProxyConverter(IEnumerable<Type> entityTypes, bool ignoreNulls = true) : base()
{
_EntityTypes = entityTypes;
_IgnoreNulls = ignoreNulls;
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return _EntityTypes;
}
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var result = new Dictionary<string, object>();
if (obj == null)
{
return result;
}
var properties = obj.GetType().GetProperties(
System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.GetProperty
);
foreach (var propertyInfo in properties.Where(p => Attribute.GetCustomAttributes(p, typeof(ScriptIgnoreAttribute), true).Length == 0))
{
if (!propertyInfo.Name.StartsWith("_"))
{
var value = propertyInfo.GetValue(obj, null);
if (value != null || !_IgnoreNulls)
{
result.Add(propertyInfo.Name, propertyInfo.GetValue(obj, null));
}
}
}
return result;
}
}
您必须传入类(让我们记住,这些类是动态生成的代理类)才能使用它,很遗憾,因此对于任何合理的对象图来说,它可能都失败得很惨,但对于简单的单个对象和数组之类的东西,它可以工作。使用JsonResult也会失败,因为这些覆盖不能在那里使用。