检测到一个循环引用EF 6

时间:2023-01-19 20:50:00

Ok, I am getting this error:

我得到了这个错误

A circular reference was detected while serializing an object of type 'System.Data.Entity.DynamicProxies.Asset_AED71699FAD007BF6F823A5F022DB9888F62EBBD9E422BBB11D7A191CD784288'.

在序列化类型为“system . data . entity . dynamicproxies.asset_aed71699fad007bf6f823a5f822db9888f62ebbd9422bbb11d7a191cd784288”的对象时,检测到一个循环引用。

I get what it means. My code was generated from VisualStudioTools and it generated all my POCOs with virtual navigation properties and mapped the relationships. I am happy with that and I want the lazy loading.

我明白它的意思了。我的代码是由VisualStudioTools生成的,它生成具有虚拟导航属性的所有POCOs并映射关系。我对此很满意,我想要懒惰的负担。

Here is an example of my user class:

下面是我的用户类的一个例子:

public partial class User : IdentityUser
{
    public User()
    {
        this.Assets = new List<Asset>();
        this.Categories = new List<Category>();
        this.Collections = new List<Collection>();
        this.Comments = new List<Comment>();
        this.LocalIntegrations = new List<LocalIntegration>();
        this.Pages = new List<Page>();
        this.Ratings = new List<Rating>();
        this.Themes = new List<Theme>();
        this.ForbiddenCategories = new List<Category>();
        this.ForbiddenPages = new List<Page>();
    }

    public string CompanyId { get; set; }
    public string CreatedById { get; set; }
    public string ModifiedById { get; set; }
    public System.DateTime DateCreated { get; set; }
    public Nullable<System.DateTime> DateModified { get; set; }
    public System.DateTime LastLoginDate { get; set; }
    public string Title { get; set; }
    public string Forename { get; set; }
    public string Surname { get; set; }
    public string Email { get; set; }
    public string JobTitle { get; set; }
    public string Telephone { get; set; }
    public string Mobile { get; set; }
    public string Photo { get; set; }
    public string LinkedIn { get; set; }
    public string Twitter { get; set; }
    public string Facebook { get; set; }
    public string Google { get; set; }
    public string Bio { get; set; }
    public string CompanyName { get; set; }
    public string CredentialId { get; set; }
    public bool IsLockedOut { get; set; }
    public bool IsApproved { get; set; }
    public bool CanEditOwn { get; set; }
    public bool CanEdit { get; set; }
    public bool CanDownload { get; set; }
    public bool RequiresApproval { get; set; }
    public bool CanApprove { get; set; }
    public bool CanSync { get; set; }
    public bool AgreedTerms { get; set; }
    public bool Deleted { get; set; }

    public virtual Company Company { get; set; }
    public virtual User CreatedBy { get; set; }
    public virtual User ModifiedBy { get; set; }
    public virtual ICollection<Asset> Assets { get; set; }
    public virtual ICollection<Category> Categories { get; set; }
    public virtual ICollection<Collection> Collections { get; set; }
    public virtual ICollection<Comment> Comments { get; set; }
    public virtual ICollection<LocalIntegration> LocalIntegrations { get; set; }
    public virtual ICollection<Page> Pages { get; set; }
    public virtual ICollection<Rating> Ratings { get; set; }
    public virtual ICollection<Theme> Themes { get; set; }
    public virtual ICollection<Group> MemberOf { get; set; }
    public virtual ICollection<Category> ForbiddenCategories { get; set; }
    public virtual ICollection<Page> ForbiddenPages { get; set; }
}

and my group class looks like this:

我的分组是这样的:

public partial class Group
{
    public Group()
    {
        this.ForbiddenCategories = new List<Category>();
        this.ForbiddenPages = new List<Page>();
        this.Members = new List<User>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string CompanyId { get; set; }
    public bool Preset { get; set; }
    public Nullable<bool> CanEdit { get; set; }
    public Nullable<bool> CanEditOwn { get; set; }
    public Nullable<bool> CanDownload { get; set; }
    public Nullable<bool> RequiresApproval { get; set; }
    public Nullable<bool> CanApprove { get; set; }
    public System.DateTime DateCreated { get; set; }
    public bool CanSync { get; set; }
    public Nullable<System.DateTime> DateModified { get; set; }
    public string CreatedById { get; set; }
    public string ModifiedById { get; set; }
    public bool Deleted { get; set; }

    public virtual Company Company { get; set; }
    public virtual User CreatedBy { get; set; }
    public virtual User ModifiedBy { get; set; }
    public virtual ICollection<Category> ForbiddenCategories { get; set; }
    public virtual ICollection<Page> ForbiddenPages { get; set; }
    public virtual ICollection<User> Members { get; set; }
}

I read that in order to stop circular references, you could do something like this:

我读到,为了阻止循环引用,你可以这样做:

public async Task<JsonResult> Get()
{
    try
    {
        using (var service = new UserService(new CompanyService()))
        {
            var u = from user in await service.GetAll()
                    select new
                    {
                        Id = user.Id,
                        UserName = user.UserName,
                        Email = user.Email,
                        IsApproved = user.IsApproved,
                        IsLockedOut = user.IsLockedOut,
                        MemberOf = user.MemberOf
                    };

            return new JsonResult { Data = new { success = true, users = u } }; // Return our users
        }
    }
    catch (Exception ex)
    {
        return new JsonResult { Data = new { success = false, error = ex.Message } };
    }
}

but it fails when returning the users because of MemberOf. The circular reference is with the User, which has a Collection and each Asset has a User who created it.

但是由于MemberOf返回用户时,它会失败。循环引用是针对用户的,用户有一个集合,每个资产都有一个创建它的用户。

Is there a way to get it to only do lazy loading on the first POCO (in this case the User class) so that it only loads the MemberOf, Assets, etc. but not the rest (i.e. not User > Assets > User)??

是否有一种方法可以让它只在第一个POCO(在本例中是User类)上延迟加载,以便它只加载MemberOf、Assets等内容,而不加载其他内容(例如,不加载User > Assets >用户)?

Update 1

更新1

evanmcdonnal suggested that it might be an issue with the JsonResult rather than my POCO classes, so I decided to have a look if there was a way to verify that. Basically I created a JsonNetResult class that inherited from JsonResult and I overrode ExecuteResult. This is what it looks like:

evanmcdonnal认为这可能是JsonResult的问题,而不是我的POCO类,所以我决定看看是否有办法验证这一点。基本上,我创建了一个JsonNetResult类,它继承自JsonResult,而我重写了ExecuteResult。它是这样的:

public class JsonNetResult : JsonResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        var response = context.HttpContext.Response;

        response.ContentType = !String.IsNullOrEmpty(ContentType) ? ContentType : "application/json";

        if (ContentEncoding != null)
            response.ContentEncoding = ContentEncoding;

        if (Data == null)
            return;

        // If you need special handling, you can call another form of SerializeObject below
        var serializedObject = JsonConvert.SerializeObject(Data, Formatting.Indented);
        response.Write(serializedObject);
    }
}

so my method looks like this now:

我的方法是这样的

    public async Task<JsonNetResult> Get()
    {
        try
        {
            using (var service = new UserService(new CompanyService()))
            {
                var u = from user in await service.GetAll()
                        select new
                        {
                            Id = user.Id,
                            UserName = user.UserName,
                            Email = user.Email,
                            IsApproved = user.IsApproved,
                            IsLockedOut = user.IsLockedOut,
                            MemberOf = user.MemberOf
                        };

                return new JsonNetResult { Data = new { success = true, users = u } }; // Return our users
            }
        }
        catch (Exception ex)
        {
            return new JsonNetResult { Data = new { success = false, error = ex.Message } };
        }
    }

and when I run this, I get the same error as before but it shows in more details. It actually states this:

当我运行这个时,我得到了和之前一样的错误但是它显示了更多的细节。它实际上州:

An exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll but was not handled in user code

“Newtonsoft.Json”类型的一个例外。JsonSerializationException' occurred in Newtonsoft.Json.但是在用户代码中没有处理dll

Additional information: Self referencing loop detected with type 'System.Data.Entity.DynamicProxies.Asset_AED71699FAD007BF6F823A5F022DB9888F62EBBD9E422BBB11D7A191CD784288'. Path 'users[0].MemberOf[0].Company.Assets[0].Categories[0].Assets'.

附加信息:使用type ' system . data . entity . dynamicproxies.asset_aed71699fad007bf6f823a5f822db9888f62ebbd9422b11d71cd784288 '检测到的自引用循环。路径的用户[0].MemberOf[0].Company.Assets[0].Categories[0].Assets”。

Now I am going to check to see if I can change the recursion limit :(

现在我要检查一下是否可以改变递归限制:(

3 个解决方案

#1


0  

You can try adding [DataContract(IsReference = true)] to your class. This will serialise your objects using reference ids and should solve circular reference problems.

您可以尝试将[DataContract(IsReference = true)]添加到您的类中。这将使用引用id序列化对象,并解决循环引用问题。

#2


0  

Based on your update I'm going to assume the current value for RecursionLimit is the problem. The docs for that property are relatively sparse but that's okay they can be found here http://msdn.microsoft.com/en-us/library/system.web.mvc.jsonresult.recursionlimit(v=vs.118).aspx

根据您的更新,我将假定递归限制的当前值是问题所在。这个属性的文档相对来说比较稀疏,但是可以在http://msdn.microsoft.com/en-us/library/system.web.mvc.jsonresult.recursionlimit(v=vs.118).aspx找到它们

I believe you can resolve the issue by replacing your current return line with the following lines of code;

我相信您可以通过以下代码行替换当前的返回行来解决这个问题;

JsonResult retVal = new JsonResult();
retVal.RecursionLimit = 1000; //maybe parameteraize this?
reVal.Data = = new { success = true, users = u };
return retVal;

EDIT: After your update I know how easily solve the problem as your exception is now thrown by JsonConvert.SerializeObject and I've solved this before :)

编辑:更新后,我知道JsonConvert会很容易地解决这个问题。SerializeObject我之前解决过这个

Based on your update you could easily fix this by changing some code in ExecuteResult (this option I'm more confident in as I actually have it implemented in my current project, I personally have not used the JsonResult class and my previous suggestion is just me leveraging my general knowledge of json.NET). You can call an overload of SerializeObject like sample below and it will now throw when you have circular references;

根据你更新你可以很容易地解决这个问题通过修改一些代码在ExecuteResult(这个选项我更自信我有实现我现在的项目,我个人没有使用JsonResult类和我之前的建议只是我利用json.NET)的一般知识。你可以调用一个重载的SerializeObject,例如下面的示例,当你有循环引用时,它会抛出;

string json = JsonConvert.SerializeObject(YourInstanceToSerialize, Formatting.Indented,
 new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Serialize });

The available values for ReferenceLoopHandling can be found here; http://james.newtonking.com/json/help/index.html?topic=html/T_Newtonsoft_Json_ReferenceLoopHandling.htm I believe the default is 0 which is why your code is throwing. Since you already updated with those changes I think I'd advise going with the second method (I at least know it will work) but feel free to try the first one too.

可在这里找到可用于ReferenceLoopHandling的值;我相信默认值是0,这就是为什么你的代码在抛出。既然您已经对这些更改进行了更新,我认为我建议使用第二种方法(至少我知道它会有用),但是也可以尝试第一种方法。

#3


0  

Ive found turning OFF lazy loading for these queries works. Simply ensure your query selects all it needs to via "include" if needed. Then before the query:

我发现关闭这些查询的延迟加载是有效的。只需确保您的查询在需要时通过“include”来选择所需的所有内容。然后在查询:

efContext.Configuration.LazyLoadingEnabled = false;

#1


0  

You can try adding [DataContract(IsReference = true)] to your class. This will serialise your objects using reference ids and should solve circular reference problems.

您可以尝试将[DataContract(IsReference = true)]添加到您的类中。这将使用引用id序列化对象,并解决循环引用问题。

#2


0  

Based on your update I'm going to assume the current value for RecursionLimit is the problem. The docs for that property are relatively sparse but that's okay they can be found here http://msdn.microsoft.com/en-us/library/system.web.mvc.jsonresult.recursionlimit(v=vs.118).aspx

根据您的更新,我将假定递归限制的当前值是问题所在。这个属性的文档相对来说比较稀疏,但是可以在http://msdn.microsoft.com/en-us/library/system.web.mvc.jsonresult.recursionlimit(v=vs.118).aspx找到它们

I believe you can resolve the issue by replacing your current return line with the following lines of code;

我相信您可以通过以下代码行替换当前的返回行来解决这个问题;

JsonResult retVal = new JsonResult();
retVal.RecursionLimit = 1000; //maybe parameteraize this?
reVal.Data = = new { success = true, users = u };
return retVal;

EDIT: After your update I know how easily solve the problem as your exception is now thrown by JsonConvert.SerializeObject and I've solved this before :)

编辑:更新后,我知道JsonConvert会很容易地解决这个问题。SerializeObject我之前解决过这个

Based on your update you could easily fix this by changing some code in ExecuteResult (this option I'm more confident in as I actually have it implemented in my current project, I personally have not used the JsonResult class and my previous suggestion is just me leveraging my general knowledge of json.NET). You can call an overload of SerializeObject like sample below and it will now throw when you have circular references;

根据你更新你可以很容易地解决这个问题通过修改一些代码在ExecuteResult(这个选项我更自信我有实现我现在的项目,我个人没有使用JsonResult类和我之前的建议只是我利用json.NET)的一般知识。你可以调用一个重载的SerializeObject,例如下面的示例,当你有循环引用时,它会抛出;

string json = JsonConvert.SerializeObject(YourInstanceToSerialize, Formatting.Indented,
 new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Serialize });

The available values for ReferenceLoopHandling can be found here; http://james.newtonking.com/json/help/index.html?topic=html/T_Newtonsoft_Json_ReferenceLoopHandling.htm I believe the default is 0 which is why your code is throwing. Since you already updated with those changes I think I'd advise going with the second method (I at least know it will work) but feel free to try the first one too.

可在这里找到可用于ReferenceLoopHandling的值;我相信默认值是0,这就是为什么你的代码在抛出。既然您已经对这些更改进行了更新,我认为我建议使用第二种方法(至少我知道它会有用),但是也可以尝试第一种方法。

#3


0  

Ive found turning OFF lazy loading for these queries works. Simply ensure your query selects all it needs to via "include" if needed. Then before the query:

我发现关闭这些查询的延迟加载是有效的。只需确保您的查询在需要时通过“include”来选择所需的所有内容。然后在查询:

efContext.Configuration.LazyLoadingEnabled = false;