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 the following exception:
Cannot add an entity that already exists.
Is it possible to make it work without manually copying every field?
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;
Is it possible to make it work without manually copying every field?
Yes - don't manually copy every field:
You could use AutoMapper.
Set up somewhere (called once at program start):
AutoMapper.Mapper.CreateMap<MyObject, MyObject>()
// don't map the id to stop conflicts when adding into db
.ForMember(a => a.Id, a => a.Ignore());
Then call:
var newObject = AutoMapper.Mapper.Map<MyObject>(oldObject);
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;
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With