tags:

views:

505

answers:

4

Hi !

i have a situation when i cant just update original record in database, but instead make a new record, copy all fields from old and apply changes to new one. (something like this if translated to code)

var original = from _orig in context.Test where _orig.id == 5 select _orig;
Test newTest = new Test();
newTest = original;
newTest.id = 0;
context.Test.InsertOnSubmit(newTest);
context.SubmitChanges();
original.parent_id = newTest.id;
original.isActive = 0;

which gives an exception like this one : Cannot add an entity that already exists.

is it possible to make it work without manually copying every field?

Thank You!

+1  A: 

You could use reflection to iterate over the properties and set them

  foreach (var prop in original.GetType().GetProperties())
  {
     prop.SetValue(newTest, prop.GetValue(original,null), null);
  }

Obviously this will need to be expanded to be less error prone, but it could be a good start.

This will certainly have a slower runtime than if the properties were written out manually, I'd imagine.

Mike
+3  A: 

This should work:

Generic Clone() Method

This method will create a full clone of any object by serializing it. The original idea came from both here and here.

/// <summary>
/// Clones any object and returns the new cloned object.
/// </summary>
/// <typeparam name="T">The type of object.</typeparam>
/// <param name="source">The original object.</param>
/// <returns>The clone of the object.</returns>
public static T Clone<T>(this T source) {
    var dcs = new DataContractSerializer(typeof(T));
    using(var ms = new System.IO.MemoryStream()) {
        dcs.WriteObject(ms, source);
        ms.Seek(0, System.IO.SeekOrigin.Begin);
        return (T)dcs.ReadObject(ms);
    }
}

Your Code Example

Now with the help of the above extension method, your code should work if tweaked a little bit:

var original = from _orig in context.Test where _orig.id == 5 select _orig;
Test newTest = original.Clone();
newTest.id = 0;
context.Test.InsertOnSubmit(newTest);
context.SubmitChanges();
original.parent_id = newTest.id;
original.isActive = 0;
Lance McNearney
Only problem is that if any relatedEntities hang off of the original entity when cloned the EntityKey is still populated with the original keys when you Add as newTest.
Mark
@Mark - When I implemented similar functionality I would .Load() any child entities of the object I was cloning, otherwise the serializer wouldn't see them and would skip them. Once they were loaded, cloning the object worked perfectly and LINQ-to-SQL automatically inserted new child records with the new cloned parent's keys on .SubmitChanges().
Lance McNearney
+2  A: 

You may also want to look into PLINQO. It has the ability to clone, detach, attach, serialize to xml, serialize to binary, many to many relationships, etc... all right out of the box so you don't have to deal with these features that should've been included in the first place.

http://www.plinqo.com/

Eric J. Smith
A: 

Using Reflection, you can easily copy each attribute that is not DbGenerated. This method is probably not very performant, but it will work in a pinch.

public static T Clone<T>(this T source)
{
    var clone = (T)Activator.CreateInstance(typeof(T));
    var cols = typeof(T).GetProperties()
        .Select(p => new { Prop = p, Attr = (ColumnAttribute)p.GetCustomAttributes(typeof(ColumnAttribute), true).SingleOrDefault() })
        .Where(p => p.Attr != null && !p.Attr.IsDbGenerated);
    foreach (var col in cols)
        col.Prop.SetValue(clone, col.Prop.GetValue(source, null), null);
    return clone;
}
brad