2009-03-12 17 views
12

Co byłoby uważane za najlepszą praktykę w powielaniu [klonowania] jednostki LINQ do SQL, powodując powstanie nowego rekordu w bazie danych?Duplikat LINQ do encji/rekordu SQL?

Kontekst jest taki, że chcę utworzyć duplikat funkcji dla rekordów w siatce administratora. strona internetowa i po wypróbowaniu kilku rzeczy i oczywistych, odczytanych danych, zmień ID = 0, zmień nazwę, submitChanges() i uderzając wyjątek, lol. Myślałem, że mogę przestać i zapytać eksperta.

Chciałbym rozpocząć od pierwszego czytania rekordu, zmieniając jego nazwę, dodając prefiks "Kopia Of", a następnie zapisując jako nowy rekord.

Odpowiedz

11

Utwórz nową instancję, a następnie użyj klas odwzorowania linq wraz z odbiciem, aby skopiować wartości składowych.

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 
     } 
    } 
} 

... czy można sklonować go przy użyciu 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

przyjrzy Kristofer. – GONeale

+0

Świetna odpowiedź! To działa, rozważałem refleksję. Co myślisz o tym rozwiązaniu? Przypuszczam, że każdy wbudowany Clone(), którego oczekiwałbym, wykonałby podobną operację. – GONeale

+1

Jeśli posiadasz właściwości tylko do odczytu w twoim podmiocie, musisz sprawdzić, czy właściwość ma ustawcę przed wywołaniem propInfo.SetValue. Możesz to zrobić, sprawdzając, czy propInfo.GetSetMethod() zwraca wartość inną niż null. – pbz

3

utknąłem z tym samym problemem i kodu Kristofer pracował idealnie, wielkie dzięki!

Jeśli ktoś jest zainteresowany, nieznacznie zmodyfikowałem jego kod, aby zamiast przyjmować obiekt docelowy jako parametr, tworzy on nowy obiekt i zwraca go. Również zrobiłem parametr sourceEntity być ogólna:

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

Wtedy mogę wykonać następujące czynności:

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

Jeśli załadować podmiot z DataContext z zestawem ObjectTrackingEnabled false następnie można wstawić podmiot ten nowa w innym 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

Dziękuję bardzo, To jest najlepsze rozwiązanie. – JanBorup

+0

Tak, to najlepsze rozwiązanie. Dzięki! – Rudy

+0

Znalazłem, że również sklonował obiekty podrzędne, coś, czego nie chciałem. Nadal uważam, że to najlepsze rozwiązanie. Linq do SQL powinien mieć wbudowaną elegancką metodę. – Rudy

0

Oto on sam roztwór jak Peter K., na LINQ-SQL Entiry Clone w 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

W Entity Framework 6 można to zrobić

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