2009-03-12 17 views
12

¿Cuál sería la mejor práctica para duplicar [clonar] una entidad LINQ to SQL que da como resultado un nuevo registro en la base de datos?Duplicate LINQ to SQL entidad/registro?

El contexto es que deseo hacer una función duplicada para los registros en una grilla de un administrador. sitio web y después de probar algunas cosas y lo obvio, leer datos, alterar ID = 0, cambiar el nombre, submitChanges(), y golpear una excepción, lol. Pensé que podría parar y preguntarle a un experto.

Deseo comenzar con la primera lectura del registro, alterando el nombre con el prefijo "Copiar de" y guardando como un nuevo registro.

Respuesta

11

Crea una nueva instancia y luego usa las clases de mapeo linq junto con la reflexión para copiar los valores de los miembros.

E.g.

public static void CopyDataMembers(this DataContext dc, 
            object sourceEntity, 
            object targetEntity) 
{ 
    //get entity members 
    IEnumerable<MetaDataMember> dataMembers = 
     from mem in dc.Mapping.GetTable(sourceEntity.GetType()) 
           .RowType.DataMembers 
     where mem.IsAssociation == false 
     select mem; 

    //go through the list of members and compare values 
    foreach (MetaDataMember mem in dataMembers) 
    { 
     object originalValue = mem.StorageAccessor.GetBoxedValue(targetEntity); 
     object newValue = mem.StorageAccessor.GetBoxedValue(sourceEntity); 

     //check if the value has changed 
     if (newValue == null && originalValue != null 
      || newValue != null && !newValue.Equals(originalValue)) 
     { 
      //use reflection to update the target 
      System.Reflection.PropertyInfo propInfo = 
       targetEntity.GetType().GetProperty(mem.Name); 

      propInfo.SetValue(targetEntity, 
           propInfo.GetValue(sourceEntity, null), 
           null); 

      // setboxedvalue bypasses change tracking - otherwise 
      // mem.StorageAccessor.SetBoxedValue(ref targetEntity, newValue); 
      // could be used instead of reflection 
     } 
    } 
} 

... o puede clonar usando DataContractSerializer:

internal static T CloneEntity<T>(T originalEntity) where T : someentitybaseclass 
{ 
    Type entityType = typeof(T); 

    DataContractSerializer ser = 
     new DataContractSerializer(entityType); 

    using (MemoryStream ms = new MemoryStream()) 
    { 
     ser.WriteObject(ms, originalEntity); 
     ms.Position = 0; 
     return (T)ser.ReadObject(ms); 
    } 
} 
+0

a echar un vistazo Kristofer. – GONeale

+0

¡Gran respuesta! Esto está funcionando, contemplé la reflexión. ¿Cómo te sientes con respecto a esta solución? Supongo que cualquier clon incorporado() que esperaba realizaría una operación similar. – GONeale

+1

Si tiene propiedades de solo lectura en su entidad, debe verificar si la propiedad tiene un setter antes de llamar a propInfo.SetValue. Puede hacer eso comprobando si propInfo.GetSetMethod() devuelve un valor no nulo. – pbz

3

me quedé con el mismo problema y el código de Kristofer funcionaba perfectamente, muchas gracias!

En caso de que alguien esté interesado, modifiqué ligeramente su código para que, en lugar de aceptar la entidad objetivo como parámetro, cree un nuevo objeto y lo devuelva. También he hecho el parámetro sourceEntity a ser genérica:

public static T CloneEntity<T>(this DataContext dc, T sourceEntity) where T:class, new() 
{ 
    var targetEntity = new T(); 
    //... original method code... 
    return targetEntity; 
} 

entonces puedo hacer lo siguiente:

dataContext.MyEntities.Attach(dataContext.CloneEntity(theEntity)); 
11

Si carga entidad de la DataContext con juego ObjectTrackingEnabled en false entonces se puede insertar esta entidad como nuevo en otro DataContext

DataContext db1 = new DataContext(); 
DataContext db2 = new DataContext(); 

db2.ObjectTrackingEnabled = false; 

MyEntity entToClone = db2.Single(e => e.Id == id); 

// ... change some data if it is needed 

db1.MyEntities.InsertOnSubmit(entToClone); 
db1.SubmitChanges(); 
+0

Muchas gracias, esta es la mejor solución. – JanBorup

+0

Sí, esta es la mejor solución. ¡Gracias! – Rudy

+0

Descubrí que también clonaba objetos secundarios, algo que no quería. Sigo pensando que esta es la mejor solución. Linq to SQL debería tener un método elegante incorporado para esto. – Rudy

0

Aquí está él misma solución que Peter K., para la Entiry Clon LINQ to SQL en VB.Net.

Dim DC As New DataContext 
    Dim DCI As New DataContext 
    DC.ObjectTrackingEnabled = False 

     Dim RGF As sf_RuleGroup = (From G In DC.sf_RuleGroups _ 
            Where G.RuleGroupID = CInt(e.CommandArgument) _ 
            Select G).First() 
     DCI.sf_RuleGroups.InsertOnSubmit(RGF) 
     DCI.SubmitChanges() 
0

En el marco Entidad 6, se pueden hacer esto

Dim WorkoutNew As New Workout 'create a record 
ff7db.Workouts.Add(WorkoutNew) 'add it to the table (workout) 
ff7db.Entry(WorkoutNew).CurrentValues.SetValues(WorkoutsPost) ' update its values with a class with the same properties