locked
How to Attach or SubmitChanges to DataContext using Singleton Repository RRS feed

  • Question

  • I am implementing a Singleton Repository class with methods that are required to open and dismiss the datacontext for every call. I call these methods from the main application as:
            
    `MyTemplate tmp1 =  Repository.Instance.Function1 (10);`

    Somewhere at the end, I want to have a Save method to save a new "MyTemplate". How is that done with the format my design is based on?

             tmp1.property1 = "change to something else";
             tmp1.property10 = "changed"; // .... and many complicated properties attached here

              Repository.Instance.Save(tmp1);  // Need help implementing!

    I also have tried "Detach()" examples online as well as "Cloning", but nothing seem to work for me.

    Please be advised I am supposed to keep this design, so options like global Datacontext is not valid. The table "MyTemplate" has a very complicated long
    list of properties so it's not as easy as working with 2 properties or copying/cloning the template.

        public sealed class Repository: IRepository, IDisposable
        {
        private static readonly Repository _instance = new Repository();

        public static Repository Instance { get { return _instance; } }
       
        private Repository() { }  // Constructors
        static Repository() { }

        // IRepository member examples:
        public MyTemplate Function1(int i)
        {  // Open and dismiss
          DataContext db = new DataContext(@"....");
          return db.mytemplates.where(x => x.Id == i).FirstOrDefault();
        }
        public MyTemplate Function2()
        {  // Open and dismiss
          DataContext db = new DataContext(@"....");
          return db.someothercommand.....
        }

        // NEED HELP HERE:
        public Save (MyTemplate templateToSave) {

          // I have tried the following:
          //    Cloning didn't work
          //    Created Partial class and implemented Detach() didn't work.

          //  Tried:
          //  db.MyTemplates.Attach(templateToSave);
          //  db.SubmitChanges();
        }
    }

    Any help would really be appreciated. I have been trying different approaches for days now.

    Thank you.

    Monday, September 2, 2013 9:04 PM

All replies

  • Hi codoiso;

    Because you are creating a new DbContext each time you call a function you need to Detach the record/s before leaving the function. Then when you pass the entity to be saved you need to Attach the record/s and change it state to modified. The Attach method call adds the entity to the context in the UnChanged state and therefore if you do a SaveChanges before changing the State to Modified no update will occur. 

    // IRepository member examples:
    public MyTemplate Function1(int i)
    {  // Open and dismiss
      DataContext db = new DataContext(@"....");
      var myTemplate = db.mytemplates.where(x => x.Id == i).FirstOrDefault();
      // This wwill detache the entity from the context.
      db.Entry(myTemplate).State = EntityState.Detached;
      return myTemplate;
    }
    
    // A generic method to handle attaching and saving changes to the db. 
    public void Save<T>(T templateToSave) where T : class 
    {
        DataContext db = new DataContext(@"....");
    
        db.Set<T>().Attach(templateToSave);
        db.Entry(templateToSave).State = EntityState.Modified;
        db.SaveChanges();
    }

    To call the Save function you need to pass the table you wish to use as the T parameter, something like this

    Save<MyTemplates>(templateToSave);

      


    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.



    Tuesday, September 3, 2013 3:00 AM
  • I realized I don't have "Entry" within my databasecontext. I have a databasecontext deriving from DataContext, so I guess it is not available within my own datacontext.

    We have a partial class like this and we use "ourDataContext" throughout rather than "DataContext" like you refer to above.

    public partial class ourDataContext : System.Data.Linq.DataContext

    Is there a way I can still implement your solution?

    Tuesday, September 3, 2013 6:17 AM
  • Hi codoiso;

    Well I should have posted a question asking what technology you were using before answering the question, but I made an assumption, my bad.

    Well you may have an issue doing what you want with Linq to SQL. A long time ago I ran into this issues using Linq to SQL and it seems like it is not working as the documentation states. I have tested this in a couple of ways with no luck. Here is some post which I ran across while trying to get this to work with Linq to SQL.

    LINQ to SQL and attaching Entities

    Although this following link will allow you to attach the object it needs to be modified after it attached in order for it to work because I have not found any way to change the state of the object.

    Detaching Entities in Linq2Sql

    I am going to keep looking and will let you know if I find anything.


    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Tuesday, September 3, 2013 11:39 PM
  • Yes, serialization didn't work for me, for weird reason.

    Please do so if you run into something.

    Thank you.

    Wednesday, September 4, 2013 1:24 AM
  • Hi codoiso;

    I think I found a solution, At least it works on my system and it should work for you. I use the serialization and deserialization technique to get a fresh copy of the entity without any tracking information. Then when I go to save changes I attached the new entity I refresh the data context with the information in the data context winning out and then do a save changes. Here is a code sample of how I did it.

    // Class variable to hold the record of which I have detached from the first dataa contex
    private Employee employee = null;
    
    private void button1_Click(object sender, EventArgs e)
    {
    	  // Create the data context, first one
        var db = new DataClasses1DataContext();
     
        // Query for the record to be detached
        Employee emp = (from em in db.Employees
                        where em.LastName == "Soto"
                        select em).FirstOrDefault();
    
        // Detach the record from the first data context.
        var empDetach = Detach<Employee>(emp);
        // Now that the record modify it between now and attaching it to the second data context.
        empDetach.FirstName = "Fernando";
        // Store the record so later it can be attached and saved back to the database.
        employee = empDetach;
    }
    
    // The next three methods will detach the entity by serializing and deserializing it.
    public T Detach<T>(T entity) where T : class 
    {
        string detachEntity = Serialize(entity);
        return (T)Deserialize(typeof(T), detachEntity);
    }
    
    private string Serialize(object entity)
    {
        if (entity.GetType() == typeof(string))
            return entity.ToString();
    
        StringWriter stringWriter = new StringWriter();
        using (XmlWriter writer = XmlWriter.Create(stringWriter))
        {
            DataContractSerializer serializer = new DataContractSerializer(entity.GetType());
            serializer.WriteObject(writer, entity);
            writer.Flush();
        }
    
        return stringWriter.ToString();
    }
    
    private object Deserialize(Type type, string serializedValue)
    {
        if (type == typeof(string))
            return serializedValue;
    
        using (StringReader stringReader = new StringReader(serializedValue))
        {
            using (XmlReader reader = XmlReader.Create(stringReader))
            {
                DataContractSerializer serializer = new DataContractSerializer((type));
                object deserializedValue = serializer.ReadObject(reader);
                return deserializedValue;
            }
        }
    }
    
    // In this event I am just calling the save function on the entity
    private void button2_Click(object sender, EventArgs e)
    {
        Save<Employee>(employee);
    }
    
    public void Save<T>(T templateToSave) where T : class
    {
    	  // Create the second data context
        var db = new DataClasses1DataContext();
    
        // Attach the entity
        db.GetTable<T>().Attach(templateToSave);
        // Refresh the data context and if there is a record in the data context DO NOT OVER WRITE IT.
        db.Refresh(RefreshMode.KeepCurrentValues, templateToSave);
        // Save the changes. Done.
        db.SubmitChanges();
    }
    You will also need to add this reference to your project
    System.Runtime.Serialization.dll
    
    And these using statements
    using System.Data.Linq;
    using System.IO;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Xml;
      

    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.


    Thursday, September 5, 2013 2:34 AM
  • Hi Fernando,

    Really appreciate your help. I've tried Serialization but was not successful, however I also tried yours but keep getting Serialization error inside your Serialize method at this line: " serializer.WriteObject(writer, entity);"

    Object graph for type 'System.Data.Linq.EntitySet`1[["some column names " Version=0.0.15.0, Culture=neutral, PublicKeyToken=null]]' contains cycles and cannot be serialized if reference tracking is disabled.

    I believe the cause is the links/pointers between entities and their dependencies. Every solution online fails for me pretty much.

    I would love to find a solution that deals with entities that have links or other entities pointing to them. Do we have to disconnect the entities and then save?

    Any advice would be appreciated.

    Thanks again.!

    Thursday, September 5, 2013 4:04 PM
  •   

    Have you disabled object tracking in your DataContext? If yes can you enable it for a test to see if the serialization will occur.

      


    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Thursday, September 5, 2013 5:55 PM
  • Do I enable object tracking for:

    1. First db reference (inside your button1_click)

    or:

    2. Second db - where I'm saving (inside your Save method)

    Thursday, September 5, 2013 8:35 PM
  • In the context that you use to do the serialization and deserialization of the entity.

    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Thursday, September 5, 2013 8:40 PM
  • What is the performance speed to detach a big database and its drawbacks when using Serialize/Deserialize? It seems this method should slow down a lot the performance if a database is very big and complicated with a lot of connections among entities.
    Monday, September 9, 2013 7:38 PM
  • Hi;

    To your question :

    What is the performance speed to detach a big database and its drawbacks when using Serialize/Deserialize? It seems this method should slow down a lot the performance if a database is very big and complicated with a lot of connections among entities.

    Well first off you will not be Serializing and Deserializing the complete database only those records that the query will be returning. You will be taking a small performance hit, IMHO, but I believe that it is better the recreating all the entities by hand adding them to the new context and modifying there state.

      


    Fernando (MCSD)

    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Tuesday, September 10, 2013 3:24 PM