EntityFramework到Json的解决方法? (序列化类型对象时检测到循环引用... DynamicProxies)[复制]

时间:2023-01-19 20:58:38

This question already has an answer here:

这个问题在这里已有答案:

so heres the deal i have

所以我有这笔交易

Models

楷模

public class News
{

    public News()
    {
        this.Created = DateTime.Now;
    }

    public int Id { get; set; }       
    public string Title { get; set; }
    public string Preamble { get; set; }
    public string Body { get; set; }
    public DateTime Created { get; set; }

    public int UserId { get; set; }

    public virtual User User { get; set; }

    public int CategoryId { get; set; }
    public int ImageId { get; set; }

    public virtual Image Image { get; set; }
    public virtual Category Category { get; set; }
}

public class Image
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string ImageUrl { get; set; }
    public Byte[] ImageData { get; set; }
    public string ImageMimeType { get; set; }
}

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
}

....following models(these models are connected to the EfDbContext) connected to following repository...

....以下模型(这些模型连接到EfDbContext)连接到以下存储库...

Interface/Repository

接口/库

public class NewsRepository : INewsRepository
{
    EfDbContext context = new EfDbContext();

    public IQueryable<News> All
    {
        get { return context.News; }
    }

    public IQueryable<News> AllIncluding(params Expression<Func<News, object>>[] includeProperties)
    {
        IQueryable<News> query = context.News;
        foreach (var includeProperty in includeProperties) {
            query = query.Include(includeProperty);
        }
        return query;
    }

    public News Find(int id)
    {
        return context.News.Find(id);
    }

    public void InsertOrUpdate(News news)
    {
        if (news.Id == default(int)) {
            // New entity
            context.News.Add(news);
        } else {
            // Existing entity
            context.Entry(news).State = EntityState.Modified;
        }
    }

    public void Delete(int id)
    {
        var news = context.News.Find(id);
        context.News.Remove(news);
    }

    public void Save()
    {
        context.SaveChanges();
    }
}

public interface INewsRepository
{
    IQueryable<News> All { get; }
    IQueryable<News> AllIncluding(params Expression<Func<News, object>>[] includeProperties);
    News Find(int id);
    void InsertOrUpdate(News news);
    void Delete(int id);
    void Save();
}

In my HomeController() i got a a JsonResult metod that i want to return the context. Here is the Method

在我的HomeController()中,我得到了一个JsonResult metod,我想返回上下文。这是方法

Json Request

Json请求

    [HttpGet]
    public JsonResult GetNews()
    {
        var p = newsRepository.AllIncluding(news => news.Category, news => news.Image);
        return Json(p, JsonRequestBehavior.AllowGet);
    }

I get the following error:

我收到以下错误:

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

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

I guessed that this has something to do with the lazyloading stuff(Iam currently learning about C#) i found this article about this...

我猜这与懒加载的东西有关(Iam目前正在学习C#)我发现这篇文章关于这个......

http://hellowebapps.com/2010-09-26/producing-json-from-entity-framework-4-0-generated-classes/

http://hellowebapps.com/2010-09-26/producing-json-from-entity-framework-4-0-generated-classes/

but i didnt get it to work... what i could read about the code was that they were tryin to depth search trough the object... more than that i couldn't figure out.

但我没有得到它的工作...我能读到的关于代码的是他们试图深入搜索通过对象...更多我无法弄清楚。

my question is how to i can pass in lazyLoading objects? into json/serializer or does it not exist, any thoughts of how i can proceed?

我的问题是如何传递lazyLoading对象?进入json / serializer还是不存在,对我如何进行的任何想法?

2 个解决方案

#1


12  

Since Json is a tree-based serialization format, it has problems with references like A->B->A.
I've read somewhere that you can use ScriptIgnore attribute in your viewmodels to prevent this error. But have not tested it.

由于Json是基于树的序列化格式,因此它存在诸如A-> B-> A之类的引用的问题。我在某处读过你可以在viewmodels中使用ScriptIgnore属性来防止这个错误。但还没有测试过。

You can change your code to the following (use anonymous types) to retrieve the items successfully:

您可以将代码更改为以下(使用匿名类型)以成功检索项目:

 var p = newsRepository.AllIncluding(news => news.Category, news => news.Image)
    .Select(n => new {id = n.Id, Body = n.Body});

Include any other property you wish to display in the last Select method. This makes your Json results more lightweight too.

包括您希望在最后一个Select方法中显示的任何其他属性。这使得你的Json结果也更加轻量级。

#2


6  

To add to Kamyar's answer...

添加到Kamyar的答案......

The AllIncluding method is only available if you are using MVC scaffolding. see the following link for a listing of the method: Mvc 3 Scaffolding: the Model passed to the View throws SQL errror

只有在使用MVC脚手架时,AllIncluding方法才可用。请参阅以下链接以获取该方法的列表:Mvc 3 Scaffolding:传递给View的模型抛出SQL错误

I tried using it, but still encountered the circular reference error, since the root objects were still being returned as proxies. So I customised the method to temporarily turn off the ProxyCreationEnabled flag on the EF context, and eagerly load the specified properties listed in the method's parameter. See the following link for further details: Loading from database without proxy classes?

我尝试使用它,但仍然遇到循环引用错误,因为根对象仍然作为代理返回。所以我自定义了方法来暂时关闭EF上下文中的ProxyCreationEnabled标志,并急切地加载方法参数中列出的指定属性。有关更多详细信息,请参阅以下链接:从没有代理类的数据库加载?

In order for this to work, the query had to be performed while the setting was still off, so I had to call the query's ToList() method to perform the query, and then returned the IEnumerable, rather than IQueryable. This did the job for me.

为了使其工作,必须在设置仍处于关闭状态时执行查询,因此我不得不调用查询的ToList()方法来执行查询,然后返回IEnumerable而不是IQueryable。这对我来说很重要。

Here is the method I used ("_context" is the variable name for my EF context):

这是我使用的方法(“_context”是我的EF上下文的变量名称):

public IEnumerable<TEntity> ListIncluding<TEntity>(params Expression<Func<TEntity, object>>[] includeProperties) 
    where TEntity : class
{
    bool cachedSetting = _context.Configuration.ProxyCreationEnabled;
    _context.Configuration.ProxyCreationEnabled = false;

    IQueryable<TEntity> query = _context.Set<TEntity>();
    foreach (var includeProperty in includeProperties)
    {
        query = query.Include(includeProperty);
    }
    IEnumerable<TEntity> list = query.ToList();
    _context.Configuration.ProxyCreationEnabled = cachedSetting;

    return list;
} 

This can then get called using the following syntax:

然后可以使用以下语法调用它:

IEnumerable<News> newsItems = newsRepository.ListIncluding<News>(news => news.Category, news => news.Image); 

#1


12  

Since Json is a tree-based serialization format, it has problems with references like A->B->A.
I've read somewhere that you can use ScriptIgnore attribute in your viewmodels to prevent this error. But have not tested it.

由于Json是基于树的序列化格式,因此它存在诸如A-> B-> A之类的引用的问题。我在某处读过你可以在viewmodels中使用ScriptIgnore属性来防止这个错误。但还没有测试过。

You can change your code to the following (use anonymous types) to retrieve the items successfully:

您可以将代码更改为以下(使用匿名类型)以成功检索项目:

 var p = newsRepository.AllIncluding(news => news.Category, news => news.Image)
    .Select(n => new {id = n.Id, Body = n.Body});

Include any other property you wish to display in the last Select method. This makes your Json results more lightweight too.

包括您希望在最后一个Select方法中显示的任何其他属性。这使得你的Json结果也更加轻量级。

#2


6  

To add to Kamyar's answer...

添加到Kamyar的答案......

The AllIncluding method is only available if you are using MVC scaffolding. see the following link for a listing of the method: Mvc 3 Scaffolding: the Model passed to the View throws SQL errror

只有在使用MVC脚手架时,AllIncluding方法才可用。请参阅以下链接以获取该方法的列表:Mvc 3 Scaffolding:传递给View的模型抛出SQL错误

I tried using it, but still encountered the circular reference error, since the root objects were still being returned as proxies. So I customised the method to temporarily turn off the ProxyCreationEnabled flag on the EF context, and eagerly load the specified properties listed in the method's parameter. See the following link for further details: Loading from database without proxy classes?

我尝试使用它,但仍然遇到循环引用错误,因为根对象仍然作为代理返回。所以我自定义了方法来暂时关闭EF上下文中的ProxyCreationEnabled标志,并急切地加载方法参数中列出的指定属性。有关更多详细信息,请参阅以下链接:从没有代理类的数据库加载?

In order for this to work, the query had to be performed while the setting was still off, so I had to call the query's ToList() method to perform the query, and then returned the IEnumerable, rather than IQueryable. This did the job for me.

为了使其工作,必须在设置仍处于关闭状态时执行查询,因此我不得不调用查询的ToList()方法来执行查询,然后返回IEnumerable而不是IQueryable。这对我来说很重要。

Here is the method I used ("_context" is the variable name for my EF context):

这是我使用的方法(“_context”是我的EF上下文的变量名称):

public IEnumerable<TEntity> ListIncluding<TEntity>(params Expression<Func<TEntity, object>>[] includeProperties) 
    where TEntity : class
{
    bool cachedSetting = _context.Configuration.ProxyCreationEnabled;
    _context.Configuration.ProxyCreationEnabled = false;

    IQueryable<TEntity> query = _context.Set<TEntity>();
    foreach (var includeProperty in includeProperties)
    {
        query = query.Include(includeProperty);
    }
    IEnumerable<TEntity> list = query.ToList();
    _context.Configuration.ProxyCreationEnabled = cachedSetting;

    return list;
} 

This can then get called using the following syntax:

然后可以使用以下语法调用它:

IEnumerable<News> newsItems = newsRepository.ListIncluding<News>(news => news.Category, news => news.Image);