Workaround for EntityFramework to Json? (While serializing an item of type..., a circular reference was discovered.) DynamicProxies)

asp.net-mvc entity-framework json

Question

I had a deal, so here it is.

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

The following models, which are linked to the EfDbContext and attached to the repository listed below...

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

I have a JsonResult method in my HomeController() that I want to use to return the context. This is the Approach:

Request in Json

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

I encounter the following issue:

While serializing an object of type 'System.Data.Entity, a circular reference was found. DynamicProxies.News 96C0B16EC4AC46070505EEC7537EF3C68EE6CE5FC3C7D8EBB793B2CF9BD391B3'.

I read this article regarding lazy loading (I'm now studying C#), and I figured that this had anything to do with it.

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

However, I was unable to make it work. From what I could gather from the code, they were doing a depth search across the object, but I was unable to decipher anything more.

How can I pass in lazyLoading objects is my query. Any ideas on how I may proceed? Does it already exist in json/serializer, or does it not?

1
7
8/1/2013 9:28:21 PM

Accepted Answer

Since Json is a tree-based serialization system, references like A->B->A are problematic.
According to what I've read, you may avoid this mistake by using the ScriptIgnore property in your viewmodels. nonetheless, I haven't tried it.

To correctly obtain the items, you may alter your code to the following (use anonymous types):

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

Include any other assets you want to highlight in the finalSelect method. Your Json results get lighter as a consequence.

12
9/30/2011 2:22:28 PM

Popular Answer

To expand on Kamyar's response...

Only MVC scaffolding is compatible with the AllIncluding approach. For a listing of the procedure, see the following link: Model given to View throws SQL error in Mvc 3 scaffolding

I tried utilizing it, but the root objects were still being returned as proxies, thus I kept getting the circular reference problem. Therefore, I modified the method to eagerly read the requested attributes stated in the method's argument and temporarily disable the ProxyCreationEnabled flag on the EF context. For further information, click the following link: Using a database without proxy classes to load data?

This needed to be done while the setting was still off, therefore I had to run the query by using the query's ToList() function, which then returned an IEnumerable rather than an IQueryable. For me, this worked.

This is the procedure I followed (the variable name for my EF context is "_context"):

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

Then, this may be invoked using the syntax shown below:

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


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow