none
Deep cloning of object in Entity Framework 4 with DbContext RRS feed

  • Question

  • Hi All,

    I have an requirement to clone an object and its all child object and collection. 

    Many codes are available but unfortunately all is doing this with ObjectContext and EntityObject, but I am working with code first approach using DbContext and poco entities and using Entity Framework 4.

    So I need deep cloning with this approach.

    Thanks to all in advance.

    Please help.

    Thanks,

    Sanjeet

    Monday, August 5, 2013 6:38 PM

Answers

  • Hi Sanjeet,

    According your last reply, I’m afraid that there are still some code missing for me to deal with this issue.

    Before call “SaveChanges” method, I think there was missing code such as “dbContext.Blogs.Add(newBlog);”. I add it myself and debug it, but I don’t receive any exception. I find that the value of the property “Id” of the “newBlog” instance was 1 after de-serialize, the value was same with “blog” instance. Then I call “SaveChanges”, nothing exception happened. I open database to check the new Id and find it turn to 7.

    Entity automatically help me correct the value. I used Visual Studio 2012 and Entity Framework5.

    Let’s do a simple test.

     

    using (var db = new AddressContext())
                {
                    var newAddress = new Address();
                    Console.WriteLine(newAddress.AddressID);
                    db.LML_Address.Add(newAddress);
                    db.SaveChanges();
                    Console.WriteLine(newAddress.AddressID);
                }


    I generated an instance of “Address” class, but not set the value of the instance. The framework will help us to set default value to 0. I add the instance to Entity then call the “SaveChanges” method. The Entity will translate the instance to SQL statement then insert into table. When the Entity return, you could see the value of the “AddressID” property is change to 9. Because SqlServer has completed the insert into statement, the value in table was 9.

    Note: Before call “SaveChanges”, I have eight rows in table.

    I’d like you to refer to our MSDN reference below to know Entity States and SaveChanges it works.

    http://msdn.microsoft.com/en-us/data/jj592676.aspx

    Return to our case, I suggest you to manually modify the value of “Id”. I have some code to fix your issue, you could try this code below:

     

    Blog newBlog = (Blog)serializer.ReadObject(xrd, false, new SerializationTypeResolver());
                        newBlog.Id = db.Blogs.Select(b => b.Id).Max() + 1;

     

    If this doesn’t work, I am afraid you should send me your whole project and database without any confidential information or business logic. I suggest you to use SkyDrive to disclose a link for me.

    Best Regards,

    Thursday, August 15, 2013 9:28 AM
    Moderator

All replies

  • Hi Sanjeet,

    Thank you for posting in our MSDN Community, I will be working with you on this post.

    From your description, I notice the issue is with doing deep cloning without Serialization. This is because “DbContext” class does not support Serialization.

    I would like to suggest a fix to this problem. Please take a look at the reference below:

    C# Object Cloning Machinery

    http://rubenhak.com/?p=70

    I hope this will help resolve your problem. If anything is unclear, please free feel to let us know.

    Best Regards,

    Wednesday, August 7, 2013 1:09 AM
    Moderator
  • Thanks Hetro,

    The solution given in the link is okay but I can not make changes in my models.

    Object graph is too large so unfortunately I can not change model's classes. Although I achieved it using DataContractSerialization up to some extent. But now, I have a different problem with my requirement.

    I have to make clone of whole object graph and save it to database, but on deserializing this object, it shows me same primary key values of objects as of original objects. So on calling SaveChange() method of my DbContext it gives primarykey violation error.

    Is there a way so that I can create a fresh object graph having all new instances.


    Thanks,

    Sanjeet


    Wednesday, August 7, 2013 11:22 AM
  • Hello Sanjeet,

    According to your description of the problem, the issue you are experiencing is primary key violation.

    As we know, when you execute “SaveChanges” method, the Entity Framework will help us to change Models into SQLs. So when then deserialization is complete, you should check the result first, get rid of duplication of the primary key.

    If you still have any doubts or concerns about this issue, please send out the complete source of the project. We only need a simple sample to reproduce the problem. You can remove any confidential information or business logic from it.

    Best Regards,

    Thursday, August 8, 2013 10:07 AM
    Moderator
  • Below is source code to produce the issue I discussed about.

    Model Calsses

    ============

     public class EntityBase
        {
            public int Id { get; set; }
            public int SortIndex { get; set; }
        }

     public class Blog : EntityBase
        {
            public string Name { get; set; }
            public virtual List<Post> Posts { get; set; }
        }

     public class Post : EntityBase
        {
            public string Title { get; set; }
            public string Content { get; set; }

            [IgnoreDataMember]
            public virtual Blog Blog { get; set; }
        }


    DbContext

    ==============

           

    public class BloggingContext : DbContext
        {
            public BloggingContext()
                : base("Server=(local);Database=test;User=sa;Password=sanjeet@123;multipleactiveresultsets=True")
            {
            }

            public BloggingContext(DbConnection conn, bool ownconn)
                : base(conn, ownconn)
            {
            }


            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
            }

            public DbSet<Blog> Blogs { get; set; }
            public DbSet<Post> Posts { get; set; }

        }

    Program.cs

    ============

       class Program
        {
            static void Main(string[] args)
            {
                DbConnection dbcon = new SqlConnectionFactory().CreateConnection("Server=(local);Database=test;User=sa;Password=sanjeet@123;multipleactiveresultsets=True");

                using (var db = new BloggingContext(dbcon, true))
                {

                    Blog blog = db.Blogs.FirstOrDefault();

                    using (MemoryStream stream = new MemoryStream())
                    {
                        DataContractSerializer serializer = new DataContractSerializer(typeof(Blog));
                        using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateDictionaryWriter(XmlWriter.Create(stream)))
                        {
                            serializer.WriteObject(writer, blog, new SerializationTypeResolver());
                        }

                        stream.Position = 0;

     XmlDictionaryReader xrd = XmlDictionaryReader.CreateDictionaryReader(XmlDictionaryReader.Create(stream));
                        xrd.Read();
                        Blog newBlog = (Blog)serializer.ReadObject(xrd, false, new SerializationTypeResolver());

                    }

                }
            }

        }

    Serialized object xml

    =================

    <Blog xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d1p2="http://tempuri.com" i:type="d1p2:Blog" xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication1">
      <Id>1</Id>
      <SortIndex>0</SortIndex>
      <Name>.Net 4</Name>
      <Posts>
        <Post i:type="d1p2:Post">
          <Id>1</Id>
          <SortIndex>0</SortIndex>
          <Content i:nil="true" />
          <Title>EF 4</Title>
        </Post>
        <Post i:type="d1p2:Post">
          <Id>2</Id>
          <SortIndex>0</SortIndex>
          <Content i:nil="true" />
          <Title>Task</Title>
        </Post>
        <Post i:type="d1p2:Post">
          <Id>3</Id>
          <SortIndex>0</SortIndex>
          <Content i:nil="true" />
          <Title>Security</Title>
        </Post>
      </Posts>
    </Blog>

    ============= =================

    Now, You can see that all Id column for Blog and Post is there and when I de-serialize this object it actually create a copy of object "blog". But I need and whole new object graph (clone of "blog" object) that mean Id column value except other column should be de-serialized with new values.

    Please help me on this.

    Thanks,

    Sanjeet

    Sunday, August 11, 2013 11:32 AM
  • Hi Sanjeet,

    Thank you for your posting.

    I have successfully reproduce your code and it works fine, but I don’t know how to reproduce the primary key violation issue that you have said. Could you please provide the more information regarding the primary key violation issue?

    As I understand it, you want to copy all contents of database “A” to database “B”. When you call “SaveChanges()”, the issue raise.

    Please feel free to let me know if I have misunderstood anything.

    Best Regards,

    Monday, August 12, 2013 8:45 AM
    Moderator
  • Hi Hetro wong,

    I missed a line there in program.cs. First I create a new instance of Blog entity then I de-serialize  my blog object

    So, code should be like below.

    Blog newlog = new Blog();

    newBlog = (Blog)serializer.ReadObject(xrd, false, new SerializationTypeResolver());

    dbContext.SaveChanges(); // this line of code throw primary key violation exception.

    Also, I am trying to save this newBlog entity in the same data base. Then on calling SaveChanges() method , it throws primary key violation exception, that looks okay when I check xml of my serialized object. All Id column's values are same as of my original objects.

    I want a way if I may able to create all new instances of all related objects in Blog object graph.

    As I have to create a new object graph using the existing one.

    Thanks,

    Sanjeet

    Tuesday, August 13, 2013 5:38 AM
  • Hi Sanjeet,

    According your last reply, I’m afraid that there are still some code missing for me to deal with this issue.

    Before call “SaveChanges” method, I think there was missing code such as “dbContext.Blogs.Add(newBlog);”. I add it myself and debug it, but I don’t receive any exception. I find that the value of the property “Id” of the “newBlog” instance was 1 after de-serialize, the value was same with “blog” instance. Then I call “SaveChanges”, nothing exception happened. I open database to check the new Id and find it turn to 7.

    Entity automatically help me correct the value. I used Visual Studio 2012 and Entity Framework5.

    Let’s do a simple test.

     

    using (var db = new AddressContext())
                {
                    var newAddress = new Address();
                    Console.WriteLine(newAddress.AddressID);
                    db.LML_Address.Add(newAddress);
                    db.SaveChanges();
                    Console.WriteLine(newAddress.AddressID);
                }


    I generated an instance of “Address” class, but not set the value of the instance. The framework will help us to set default value to 0. I add the instance to Entity then call the “SaveChanges” method. The Entity will translate the instance to SQL statement then insert into table. When the Entity return, you could see the value of the “AddressID” property is change to 9. Because SqlServer has completed the insert into statement, the value in table was 9.

    Note: Before call “SaveChanges”, I have eight rows in table.

    I’d like you to refer to our MSDN reference below to know Entity States and SaveChanges it works.

    http://msdn.microsoft.com/en-us/data/jj592676.aspx

    Return to our case, I suggest you to manually modify the value of “Id”. I have some code to fix your issue, you could try this code below:

     

    Blog newBlog = (Blog)serializer.ReadObject(xrd, false, new SerializationTypeResolver());
                        newBlog.Id = db.Blogs.Select(b => b.Id).Max() + 1;

     

    If this doesn’t work, I am afraid you should send me your whole project and database without any confidential information or business logic. I suggest you to use SkyDrive to disclose a link for me.

    Best Regards,

    Thursday, August 15, 2013 9:28 AM
    Moderator