自参考循环检测到EF / Json

时间:2014-01-16 16:45:00

标签: c# json entity-framework

我正在使用Entity Framework(代码优先),我已经映射了所有实体并禁用了延迟加载。 这很好用,但是当我尝试做一些 Ajax 工作时,我就开始遇到问题了。

第一个问题是我的用户类。这看起来像这样:

public partial class User : IdentityUser
{
    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 Company Company { get; set; }
    public User CreatedBy { get; set; }
    public User ModifiedBy { get; set; }
    public ICollection<Asset> Assets { get; set; }
    public ICollection<Category> Categories { get; set; }
    public ICollection<Collection> Collections { get; set; }
    public ICollection<Comment> Comments { get; set; }
    public ICollection<LocalIntegration> LocalIntegrations { get; set; }
    public ICollection<Page> Pages { get; set; }
    public ICollection<Rating> Ratings { get; set; }
    public ICollection<Theme> Themes { get; set; }
    public ICollection<Group> MemberOf { get; set; }
    public ICollection<Category> ForbiddenCategories { get; set; }
    public ICollection<Page> ForbiddenPages { get; set; }
}

现在,由于延迟加载已停用,我使用预先加载,如果我在调用此方法时查看此内容:

    // 
    // AJAX: /Users/Get
    public JsonResult Get()
    {
        try
        {
            using (var uow = new UnitOfWork<SkipstoneContext>())
            {
                var service = new UserService(uow, User.Identity.GetUserId(), this.CompanyId);
                var u = service.GetAll("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 } };
        }
    }

我可以清楚地看到只填充成员导航属性。其余的按预期为空。

如果我尝试将传递给我的 JsonResult ,则会出现问题。 我收到错误:

  

为类型为“System.Data.Entity.DynamicProxies.User_E9B58CAAA82234358C2DE2AF8788D33803C4440F800EA8E015BE49C58B010EDF”的属性“CreatedBy”检测到自引用循环。路径'用户[0]'。

在另一篇文章中,我读到这是因为Json.Net尝试序列化并填充所有导航属性。

所以,我创建了一个新项目,并创建了两个模型:

public class User
{
    public User()
    {
        this.Id = Guid.NewGuid().ToString();
    }

    public string Id { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public string CreatedById { get; set; }

    public ICollection<Post> Posts { get; set; }
    public User CreatedBy { get; set; }
}

public class Post
{
    public string Id { get; set; }
    public string Title { get; set; }
    public string Message { get; set; }
    public string CreatedById { get; set; }

    public User CreatedBy { get; set; }
}

我希望运行它会出现自我引用错误,因为用户不仅有发布,还有用户创造了它。

所以方法如下:

public JsonResult Get()
{
    try
    {
        var user = new User()
        {
            UserName = "moo",
            Email = "moo@moo.co.uk"
        };

        return new JsonResult { Data = new { sucess = true, user = user } };
    }
    catch (Exception ex)
    {
        return new JsonResult { Data = new { sucess = true, error = ex.Message } };
    }
}
再次,这与第一个项目完全相同(对我而言)。 无论如何,当我运行它时,项目运行良好。

有人可以向我解释为什么会这样,以及我可以做些什么来解决它?

1 个答案:

答案 0 :(得分:1)

我假设您的导航属性实际上标记为virtual,否则EF不会尝试延迟加载它们......

只是嵌套在对象的虚拟属性中的相同类型的导航属性不会使其自引用。当属性为null,空集合,或者当JSON序列化程序在遍历所有实体的属性后“找到出路”时,它就不是自引用的。自我引用意味着我从用户走向帖子,然后再回到同一个用户,再次发帖,等等。

要解决此问题,请勿尝试序列化实体。相反,首先将它们转换为视图模型,然后序列化视图模型。

public class UserViewModel
{
    public string Id { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public string CreatedById { get; set; }
    public ICollection<PostViewModel> Posts { get; set; }
    public string CreatedByUserName { get; set; } // end self-referencing
}

public class PostViewModel
{
    public string Id { get; set; }
    public string Title { get; set; }
    public string Message { get; set; }
    public string CreatedById { get; set; }
    public string CreatedByUserName { get; set; } // end self-referencing
}

public JsonResult Get()
{
    try
    {
        using (var uow = new UnitOfWork<SkipstoneContext>())
        {
            var service = new UserService(uow, User.Identity.GetUserId(), this.CompanyId);
            var u = service.GetAll("MemberOf");
            var vm = Mapper.Map<UserViewModel[]>(u);
            return new JsonResult { Data = new { success = true, users = vm } }; // Return our users
        }
    }
    catch (Exception ex)
    {
        return new JsonResult { Data = new { success = false, error = ex.Message } };
    }
}
相关问题