Serialization of Entity Framework objects with One to Many Relationship

Blake Blackwell picture Blake Blackwell · Oct 25, 2012 · Viewed 46.8k times · Source

I am attempting to use EF with Code First and the Web API. I don't have any problems until I get into serializing Many-to-Many relationships. When I attempt to execute the following web api method below I get the following error message:

public class TagsController : ApiController
{

        private BlogDataContext db = new BlogDataContext();

        // GET api/Tags
        public IEnumerable<Tag> GetTags()
        {
            return db.Tags.AsEnumerable();
        }
}

I get the following error:

'System.Data.Entity.DynamicProxies.Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D' with data contract name 'Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.

I have read some SO articles (article 1, article 2) that the fix is to add the following attribute:

[DataContract (IsReference=true)]

but this has had no effect. Also using [IgnoreDataMember] does not have an effect. The only option that does seem to work is to set Configuration.ProxyCreationEnabled to false. Is this my only option? Am I missing something?

Sample POCO objects:

Tag

[DataContract(IsReference = true)]
public class Tag
{
        public Tag()
        {
            this.Blogs = new HashSet<Blog>();
        }

        [Key]
        [DataMember]
        public int Id { get; set; }

        [DataMember]
        public string Name { get; set; }

        [IgnoreDataMember]
        public virtual ICollection<Blog> Blogs { get; set; }
}

Blog

[DataContract(IsReference = true)]
public class Blog
{
    public Blog()
    {
        this.Tags = new HashSet<Tag>();
    }

    [Key]
    [DataMember]
    public int Id { get; set; }

    [DataMember]
    public string Name { get; set; }

    [IgnoreDataMember]
    public virtual ICollection<Tag> Tags { get; set; }
}

Answer

Erik Philips picture Erik Philips · Oct 25, 2012

When you see an object like:

System.Data.Entity.DynamicProxies.Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D

It is a runtime EF Generated version of a proxy to what would normally be considered a POCO object.

Entity Framework has created this object because it tracks when the objects has changed so when you call .SaveChanges() it can optimize what to do. The downfall of this is that you aren't actually using the specific object you defined, thus Data Contracts and Frameworks (Json.net) cannot use them as they would your original POCO object.

To Prevent EF from returning this object you have two choices (ATM):

First, Try turning off Proxy object creation on your DbContext.

DbContext.Configuration.ProxyCreationEnabled = false;

This will completely disable the create of Proxy objects for every query to the specific DbContext. (This does not affect the cached object in the ObjectContext).

Secondly, use EntityFramework 5.0+ with AsNoTracking() (ProxyCreationEnabled is still available in EF 5.0 as well)

You should also be able to

DbContext.Persons.AsNoTracking().FirstOrDefault();

or

DbContext.Persons.
  .Include(i => i.Parents)
  .AsNoTracking()
  .FirstOrDefault();

Instead of globally disabling proxy creation for the DbContext, this only turns it off per query. (This DOES affect the cached object in the ObjectContext, it is not cached)