Merhabalar, son günlerde kafamı oldukça meşgul eden bir konu hakkında bulduğum çözümü paylaşmak istiyorum. Senaryom şöyleydi: Halihazırda varolan bir kayıtın kopyasını alıp üzerinde bir kaç değiklik yapıp yeni kayıt olarak tekrar veritabanına kayetmem gerekiyordu. Bu işlemi yapmam gereken sınıf aşağıdaki gibidir:

Yine senaryoya göre bir arıza kaydı ilgili teknik personel tarafından sevk edilebilir işlevselliğini sağlamalıydı. Ancak sevk işlemi yapılırken ilgili kayıt kapatılmalı ve sevk edilen teknik personel'e yeni arıza kaydı olarak düşmeliydi. Bu durum için akla gelecek basit durumları açıklığa kavuşturmakta fayda var. İlk etapda bir akla gelebilecek çözümlerden bahsedelim.
1- Veritabanından ilgili kayıtın çekilmesinin ardından gerekli alanlar değiştirilir, yeni bir ArizaID verilir ve linq to sql'e al bunu ekle(InsertOnSubmit) denebilir ancak böyle bir işlevsellik malesef mevcut değil ve bu kayıt zaten var diye bir hata döndermesi çok olası oluyor.
2- Diğer bir yöntem de bir method oluşturup ariza sınıfının örneğini göndermemiz ve ilgili kayıttaki her bir alanı tek tek yeni arıza örneğine atamamız olabilir. Bu az önceki sorunu çözecektir ancak bir programcı böyle bir çözüm üretmemelidir diyerekten bunu es geçiyoruz. Çünkü tablo yapısında gerçekleşecek değişiklikler sonucunda devasa bug potansiyeli yüksek kod bloğumuz patlamaya hazır hale getirilmiş bomba gibi olacaktır.
3- 2. Akıla gelen method'tan yola çıkarak arıza sınıfını dinamik bir şekilde kopyalayıp ekleme yöntemine gidebiliriz. Bunun için izlenebilecek çeşitli yöntemler de mevcut ancak linqtosql ile çalıştığımızı unutmamakta da fayda var.
Kopayala işlemini yapacak aşağıdaki metottan bahsediyor olacağım. Methodlar alıntı olmakla beraber dayanıklı olduğunu ifade edebilirim. Metot parametre olarak altığı source değişkenini DataContractSerializer sınıfı yardımıyla memory'den okuyup yeni değişkenin memory adresine yazıyor. Böylece yeni bir nesne değeri hafızadakini yerini alıyor ve sonuç olarak bize yeni değişkeni dönderiyor. Böylece nesnenin birebir kopyasını elde etmiş oluyoruz. Kodlama dilinde vb.net de kullanmaya başladığımdan ötürü kodların vb.net karşılıklarını okuyor olacaksınız.
<Extension()> _
Public Function CloneEntity(Of T)(ByVal source As T) As T
Dim dcs = New System.Runtime.Serialization.DataContractSerializer(GetType(T))
Using ms = New System.IO.MemoryStream()
dcs.WriteObject(ms, source)
ms.Seek(0, System.IO.SeekOrigin.Begin)
Return DirectCast(dcs.ReadObject(ms), T)
End Using
End Function
İlgili metot elimizde olduğuna göre şöyle küçük bir kod örneğiyle neler olduğuna bakalım.
Dim db As New DBDataContext()
Dim oldArz As Ariza = (From p In db.Arizas
Where p.ArizaID = arizaID
Select p).FirstOrDefault()
If oldArz Is Nothing Then
Throw New InvalidOperationException("Arıza kaydı bulunamadı")
End If
Dim newArz As Ariza = oldArz.CloneEntity() ' Arızanın kopyası oluşturuluyor
oldArz.ArizaDurum = ArizaDurum.SevkEdildi
newArz.ArizaID = Guid.NewGuid()
newArz.ArizaDurum = ArizaDurum.IslemBekliyor
db.Arizas.InsertOnSubmit(newArz) ' Yeni arıza kaydı ekleniyor
db.SubmitChanges() 'Eski arıza kaydı gerekli düzenlemeler sonrasında yenisiyle birlikte kaydediliyor
Bu kod parçasını yazdıktan sonra da çalıştırdığımızda tam da CopyEntity methodu çağırıldığı sırada aşağıdaki hatayı alıyor olacağız.
"Object graph for type 'Ariza' contains cycles and cannot be serialized if reference tracking is disabled."
Bu hatayı almamızın asıl sebebi Ariza sınıfının temsil ettiği tablo ile ilişkilendirilmiş diğer tabloların varlığıdır. LinqToSql tablo sınıflarını oluşturuken her bir ilişki için ilgili sınıfa ilişkili tablo sınıfının koleksiyon karşılığını ekler. Bu nedenle de DataContractSerializer bu sınıfı memory'den okuyup serileştirmek istediğinde yukarıdaki hatayı fırlatır. Yapılması gereken şey linq to sql nesnesinin serileştirme modunun none olan değerinin Unidirectional(tek yönlü) olarak değiştirilmesidir. Bu işleminde ardından artık yazdığımız kod sorunsuz çalışmaya başlayacaktır.

Bazıları 2. yöntemi tercih edebilir ancak sağlıksız kod yazılmış olacaktır ayrıca benzer şekilde farklı tablolarında bu tarz bir işleme maruz kalması gerekirse hepsi için benzer kodun yazılması gerekecektir. Bu da daha kötü kodların ortalıkta gezmesine sebep olacaktır. Özetle, ikinci methodu yazmamak için 3 gün bekledim ve istediğim sonuca ulaştım. Umarım beklemek için vaktiniz ve bunu anlayışla karşılayabilecek yöneticileriniz vardır. Çünkü bu süre boyunca kodlamanın bu kısımı felç oldu diyebilirim :) Sonuç olarak gayet mutluyuz. Yaşasın güzel kod :)
Kayifli kodlamalar diliyorum.
Ömer Faruk ZORLU
2d61a324-0150-4ca7-af31-af6ffaaf71d9|0|.0