2

How can I serialize an Entity Framework Code First Proxy when the class it is proxying is decorated with DataContractAttribute(IsReference = true)?

When using DataContractSerializer with ProxyContractResolver, I get the following:

The IsReference setting for type 'System.Data.Entity.DynamicProxies.MyType_59A83378572C10D0B31B6892FB6C3E7428C4BA214322C7A77BD5E1EB19E529CA' is 'False', but the same setting for its parent class 'My.Namespace.MyType' is 'True'. Derived types must have the same value for IsReference as the base type. Change the setting on type

It seems that the EF Proxy Generator is not respecting the IsReference attribute, which I do need on the POCO (otherwise I end up with a stack overflow due to mutual referencing).

Is there any mechanism at all to serialize an EF Proxy under these circumstances? How?

Eric J.
  • 147,927
  • 63
  • 340
  • 553
  • Btw. why do you have proxied entities? It is usually recommended to avoid dynamic proxies when you want to serialize entities. – Ladislav Mrnka Jul 10 '12 at 18:44
  • Perhaps I misunderstand something, but my hope was that I could detach them from the context they are first loaded from, serialize them to a cache, and attach them later to a different context. The scenario is caching an object graph that is expensive to load that also must be associated with new objects that are being created, e.g. `MyNewObject obj = new MyNewObject() { ExpensiveToLoad = Cache["ExpensiveToLoad"] }`. – Eric J. Jul 10 '12 at 19:45
  • Detaching through serialization is a correct approach but it still doesn't explain why are you using proxies? Did you need lazy loading to build the object graph? – Ladislav Mrnka Jul 10 '12 at 20:58
  • Yes, because of this issue: http://stackoverflow.com/questions/10342709/eagerly-load-navigation-property-that-is-listofsomebaseclass – Eric J. Jul 10 '12 at 21:08

2 Answers2

4

I just did following very simple example and ProxyDataContractResolver works as expected - graph of proxied entities with cycles was correctly serialized.

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Objects;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;

namespace CFSerialization
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new Context())
            {
                context.Database.Delete();
                context.Database.CreateIfNotExists();

                context.Masters.Add(new Master
                    {
                        Name = "abc",
                        Details = new List<Detail>
                            {
                                new Detail { Name = "a" },
                                new Detail { Name = "b" },
                                new Detail { Name = "c" }
                            }
                    });
                context.SaveChanges();
            }

            using (var context = new Context())
            {
                // This will get proxied Master
                var master = context.Masters.First();

                var serializer = new DataContractSerializer(typeof(Master), new DataContractSerializerSettings()
                    {
                        DataContractResolver = new ProxyDataContractResolver()
                    });

                using (var stream = new MemoryStream())
                {
                    // This will also lazy load all details
                    serializer.WriteObject(stream, master);
                    stream.Seek(0, SeekOrigin.Begin);
                    var newMaster = (Master)serializer.ReadObject(stream);
                }
            }
        }
    }

    [DataContract(IsReference=true)]
    public class Master
    {
        [DataMember]
        public int Id { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public virtual ICollection<Detail> Details { get; set; }
    }

    [DataContract(IsReference=true)]
    public class Detail 
    {
        [DataMember]
        public int Id { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public virtual Master Master { get; set; }
    }

    public class Context : DbContext
    {
        public DbSet<Master> Masters { get; set; }
    }
}

There must be something more complicated in your model to break the functionality - are all your entities marked with DataContract(IsReference=true)?

Note: I tested it in .NET 4.5.

Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • Yes, all are marked with `IsReference=true`. They share a common base class with that attribute. I'm not using .NET 4.5 yet. Perhaps the difference lies there. – Eric J. Jul 12 '12 at 03:46
  • Is there an alternative way in .NET 4.0? – 123 456 789 0 Nov 19 '12 at 20:01
  • Not this part though, new DataContractSerializerSettings() { DataContractResolver = new ProxyDataContractResolver() }); Anyway I figured out another way to do it. :) – 123 456 789 0 Nov 19 '12 at 23:08
1

Notice that using the ProxyDataContractResolver() only works if the proxied class specifies IsReference=true directly.
It does NOT currently work if it is done on a base class as noted in the first comment above.

Stephan Bauer
  • 9,120
  • 5
  • 36
  • 58