我尝试序列化从实体数据模型.edmx自动生成的POCO类,当我使用
JsonConvert.SerializeObject
我得到了以下错误:
为System.data.entity类型检测到自我引用循环错误。
我怎么解决这个问题?
我尝试序列化从实体数据模型.edmx自动生成的POCO类,当我使用
JsonConvert.SerializeObject
我得到了以下错误:
为System.data.entity类型检测到自我引用循环错误。
我怎么解决这个问题?
当前回答
只需更新Startup.cs文件中的services.AddControllers()即可
services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
其他回答
我也面临着同样的问题,我尝试使用JsonSetting来忽略自引用错误,它的某种工作,直到我得到一个类,自引用非常深入,我的。net进程挂在Json写入值。
我的问题
public partial class Company : BaseModel
{
public Company()
{
CompanyUsers = new HashSet<CompanyUser>();
}
public string Name { get; set; }
public virtual ICollection<CompanyUser> CompanyUsers { get; set; }
}
public partial class CompanyUser
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int UserId { get; set; }
public virtual Company Company { get; set; }
public virtual User User { get; set; }
}
public partial class User : BaseModel
{
public User()
{
CompanyUsers = new HashSet<CompanyUser>();
}
public string DisplayName { get; set; }
public virtual ICollection<CompanyUser> CompanyUsers { get; set; }
}
你可以在User类中看到问题,它引用了CompanyUser类,这是一个自引用。
现在,我正在调用包含所有关系属性的GetAll方法。
cs.GetAll("CompanyUsers", "CompanyUsers.User");
在这个阶段,我的DotNetCore进程挂着执行JsonResult,写入值…永远不要来。在Startup.cs中,我已经设置了JsonOption。出于某种原因,EFCore包含了嵌套属性,我没有要求Ef给出。
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
预期的行为应该是这样的
嘿,EfCore,你能把“公司用户”数据也包括在我的 公司类,以便我可以很容易地访问数据。
then
嘿,EfCore,你能把“公司用户”也包括进来吗?用户数据为 这样我就可以像这样轻松地访问数据 .User.DisplayName Company.CompanyUsers.First ()
在这个阶段,我应该只得到这个"Company.CompanyUsers.First(). user。DisplayName"它不应该给我company。companyusers。first (). user。导致自引用问题的CompanyUsers;从技术上讲,它不应该给我User。CompanyUsers作为CompanyUsers是一个导航属性。EfCore非常兴奋,给了我user。companyusers。
所以,我决定为属性写一个扩展方法来排除对象(它实际上不是排除它只是设置属性为null)。不仅如此,它还适用于数组属性。下面是我还将为其他用户导出nuget包的代码(不确定这是否甚至有助于某人)。原因很简单,因为我懒得写.Select(n => new {n.p1, n.p2});我只是不想写选择语句只排除1个属性!
这不是最好的代码(我会在某个阶段更新),因为我写得很匆忙,尽管这可能会帮助那些想要排除(设置null)对象数组的人。
public static class PropertyExtensions
{
public static void Exclude<T>(this T obj, Expression<Func<T, object>> expression)
{
var visitor = new PropertyVisitor<T>();
visitor.Visit(expression.Body);
visitor.Path.Reverse();
List<MemberInfo> paths = visitor.Path;
Action<List<MemberInfo>, object> act = null;
int recursiveLevel = 0;
act = (List<MemberInfo> vPath, object vObj) =>
{
// set last propert to null thats what we want to avoid the self-referencing error.
if (recursiveLevel == vPath.Count - 1)
{
if (vObj == null) throw new ArgumentNullException("Object cannot be null");
vObj.GetType().GetMethod($"set_{vPath.ElementAt(recursiveLevel).Name}").Invoke(vObj, new object[] { null });
return;
}
var pi = vObj.GetType().GetProperty(vPath.ElementAt(recursiveLevel).Name);
if (pi == null) return;
var pv = pi.GetValue(vObj, null);
if (pi.PropertyType.IsArray || pi.PropertyType.Name.Contains("HashSet`1") || pi.PropertyType.Name.Contains("ICollection`1"))
{
var ele = (IEnumerator)pv.GetType().GetMethod("GetEnumerator").Invoke(pv, null);
while (ele.MoveNext())
{
recursiveLevel++;
var arrItem = ele.Current;
act(vPath, arrItem);
recursiveLevel--;
}
if (recursiveLevel != 0) recursiveLevel--;
return;
}
else
{
recursiveLevel++;
act(vPath, pv);
}
if (recursiveLevel != 0) recursiveLevel--;
};
// check if the root level propert is array
if (obj.GetType().IsArray)
{
var ele = (IEnumerator)obj.GetType().GetMethod("GetEnumerator").Invoke(obj, null);
while (ele.MoveNext())
{
recursiveLevel = 0;
var arrItem = ele.Current;
act(paths, arrItem);
}
}
else
{
recursiveLevel = 0;
act(paths, obj);
}
}
public static T Explode<T>(this T[] obj)
{
return obj.FirstOrDefault();
}
public static T Explode<T>(this ICollection<T> obj)
{
return obj.FirstOrDefault();
}
}
上面的扩展类将使您能够将属性设置为null,以避免自引用循环甚至数组。
表达式构建器
internal class PropertyVisitor<T> : ExpressionVisitor
{
public readonly List<MemberInfo> Path = new List<MemberInfo>();
public Expression Modify(Expression expression)
{
return Visit(expression);
}
protected override Expression VisitMember(MemberExpression node)
{
if (!(node.Member is PropertyInfo))
{
throw new ArgumentException("The path can only contain properties", nameof(node));
}
Path.Add(node.Member);
return base.VisitMember(node);
}
}
Usages:
模型类
public class Person
{
public string Name { get; set; }
public Address AddressDetail { get; set; }
}
public class Address
{
public string Street { get; set; }
public Country CountryDetail { get; set; }
public Country[] CountryDetail2 { get; set; }
}
public class Country
{
public string CountryName { get; set; }
public Person[] CountryDetail { get; set; }
}
虚拟数据
var p = new Person
{
Name = "Adeel Rizvi",
AddressDetail = new Address
{
Street = "Sydney",
CountryDetail = new Country
{
CountryName = "AU"
}
}
};
var p1 = new Person
{
Name = "Adeel Rizvi",
AddressDetail = new Address
{
Street = "Sydney",
CountryDetail2 = new Country[]
{
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A1" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A2" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A3" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A4" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A5" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A6" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A7" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A8" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A9" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A1" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A2" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A3" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A4" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A5" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A6" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A7" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A8" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A9" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
}
}
};
例:
情况1:只排除没有任何数组的属性
p.Exclude(n => n.AddressDetail.CountryDetail.CountryName);
案例2:排除带有1个数组的属性
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryName);
案例3:排除带有2个嵌套数组的属性
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryDetail.Explode().Name);
案例4:包含EF GetAll查询
var query = cs.GetAll("CompanyUsers", "CompanyUsers.User").ToArray();
query.Exclude(n => n.Explode().CompanyUsers.Explode().User.CompanyUsers);
return query;
你已经注意到,explosion()方法也是一个扩展方法,用于我们的表达式构建器从数组属性中获取属性。只要有数组属性,就使用. explosion()。YourPropertyToExclude或. explosion (). property1 . myarrayproperty . explosion (). mystupidproperty。上面的代码帮助我避免像我想要的那样深的自我引用。现在我可以使用GetAll并排除我不想要的属性!
感谢你阅读这篇文章!
c#代码:
var jsonSerializerSettings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
};
var jsonString = JsonConvert.SerializeObject(object2Serialize, jsonSerializerSettings);
var filePath = @"E:\json.json";
File.WriteAllText(filePath, jsonString);
对我来说,我必须走不同的路线。而不是试图修复JSON。我必须在我的数据上下文的惰性加载之后。
我刚刚添加了这个到我的基本库:
context.Configuration.ProxyCreationEnabled = false;
“context”对象是我在基本存储库中使用的构造函数参数,因为我使用依赖注入。您可以在实例化数据上下文的任何地方更改ProxyCreationEnabled属性。
http://techie-tid-bits.blogspot.com/2015/09/jsonnet-serializer-and-error-self.html
使用JsonSerializerSettings
ReferenceLoopHandling。如果遇到引用循环,Error(默认)将出错。这就是为什么会出现异常。 ReferenceLoopHandling。如果对象嵌套但不是无限的,序列化是有用的。 ReferenceLoopHandling。如果对象是自身的子对象,Ignore将不会序列化该对象。
例子:
JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
});
如果你必须序列化一个无限嵌套的对象,你可以使用PreserveObjectReferences来避免StackOverflowException。
例子:
JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented,
new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects
});
选择对要序列化的对象有意义的内容。
参考http://james.newtonking.com/json/help/
我喜欢从Application_Start()做它的解决方案,在这里的答案
显然,我不能在JavaScript中使用我的函数中的配置访问json对象,因为在DalSoft的答案中返回的对象在对象的(键,val)上都有“\n \r”。
不管怎样,只要能工作就好(因为不同的方法在不同的场景下工作,这取决于所提出的意见和问题),尽管最好采用一种标准的方法,并提供一些良好的文档支持该方法。