检测到EF 6的循环参考

时间:2014-01-08 00:08:46

标签: c# entity-framework json.net

好的,我收到了这个错误:

  

在序列化“System.Data.Entity.DynamicProxies.Asset_AED71699FAD007BF6F823A5F022DB9888F62EBBD9E422BBB11D7A191CD784288”类型的对象时检测到循环引用。

我明白这意味着什么。我的代码是从VisualStudioTools生成的,它生成了所有具有虚拟导航属性的POCO并映射了关系。 我很满意,我想要延迟加载。

以下是我的用户类的示例:

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

我的小组课程看起来像这样:

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

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

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

但由于 MemberOf 而退回用户时失败。 循环引用是用户,其中集合,每个资产都有用户创建它。

有没有办法让它只在第一个POCO(在这种情况下是用户类)上进行延迟加载,这样它只会加载MemberOf,Assets等,但不会加载其余的(即不是用户&gt;资产&gt;用户)??

更新1

evanmcdonnal建议它可能是 JsonResult 而不是我的POCO类的问题,所以我决定看一下是否有办法验证它。 基本上我创建了一个继承自 JsonResult JsonNetResult 类,并且我覆盖了 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);
    }
}

所以我的方法现在看起来像这样:

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

当我运行它时,我得到了与以前相同的错误,但它显示了更多细节。它实际上说明了这一点:

  

Newtonsoft.Json.dll中发生了'Newtonsoft.Json.JsonSerializationException'类型的异常,但未在用户代码中处理

     

附加信息:使用类型'System.Data.Entity.DynamicProxies.Asset_AED71699FAD007BF6F823A5F022DB9888F62EBBD9E422BBB11D7A191CD784288'检测到自引用循环。路径'users [0] .MemberOf [0] .Company.Assets [0] .Categories [0] .Assets'。

现在我要查看是否可以更改递归限制:(

3 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

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

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

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

编辑:更新后我知道如何轻松解决问题,因为JsonConvert.SerializeObject现在抛出了异常,我之前已经解决了这个问题:)

根据您的更新,您可以通过更改ExecuteResult中的一些代码轻松解决此问题(此选项我更有信心,因为我实际在当前项目中实现了它,我个人没有使用{{ 1}}类和我以前的建议只是我利用我对json.NET的一般知识)。您可以调用JsonResult的重载,例如下面的示例,当您有循环引用时它将抛出;

SerializeObject

string json = JsonConvert.SerializeObject(YourInstanceToSerialize, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Serialize }); 的可用值可在此处找到; http://james.newtonking.com/json/help/index.html?topic=html/T_Newtonsoft_Json_ReferenceLoopHandling.htm我认为默认值是0,这就是你的代码抛出的原因。既然您已经更新了这些更改,我想我会建议使用第二种方法(我至少知道它会起作用),但也可以尝试第一种方法。

答案 2 :(得分:0)

我发现关闭这些查询的延迟加载工作。只需确保您的查询通过&#34; include&#34;选择所需的全部内容。如果需要的话。 然后在查询之前:

efContext.Configuration.LazyLoadingEnabled = false;