开发者

Linq to sql, copy original entity to new one and save

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;
newT开发者_Python百科est.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.


It might be better if you create a stored proc and copy the objects in there instead of wasting time on linq-sql limitations. You can handle errors and other problems easily in sql syntax.


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/

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜