2012-03-14 28 views
5

Bu daha çok kavramsal bir soru için MVVM desende kayıtlarını güncellemek için. İşte benim şu anki durumum; Bir vb.net WPF uygulaması yazıyorum ve MVVM desenini kullanarak (onu seviyorum! Sürdürülebilirlik sadece inanılmaz derecede harika). Şu anda tüm kodu elle yazılmış ve arka uç bir erişim veritabanı (politikasına ben JET Veritabanları desteklemez NH ve EF kullanamazsınız nedeniyle, biz bir noktada MSSQL geçiş yapabilirler olduğu gibi NHibernate veya Varlık Framework hiçbir faydası yoktur edilir ama şu andan itibaren bir süre olabilir.uygun şekilde maksimum verim

uygulama oldukça iyi çalışıyor ve tekrar veritabanına güncellemeleri göndermek için en iyi yolu nedir merak ediyorum.

Şu anda, yöntem, "kirli" olarak modelin ayarlanan kısmındaki bir kayda bir boole eklemektir; daha sonra "kirli" olan tüm kayıtlar arasında geçiş yaparız ve oledbcommand'ı kullanırız (parametrelerle çalıştır) sql ifadeleri güncellenir.

Bu endişeleri mükemmel bir ayrım yaratır ama bu alternatifler bilmek istiyorum yanlış yol ise (veritabanı türünü ve ilişkili sakıncaları şekildedir o EF ile çalışmıyor lütfen unutmayın).

Teşekkürler!

Eğer veritabanına değiştirir işlemek için bir Güncelleme/Kaydet düğmesini kullanarak bu yana
Public Class Car 
Implements ICloneable 

Public Property Make() As String 
    Get 
     Return m_Make 
    End Get 
    Set(ByVal value As String) 
     m_Make = value 
    End Set 
End Property 
Private m_Make As String 

Public Property Model() As String 
    Get 
     Return m_Model 
    End Get 
    Set(ByVal value As String) 
     m_Model = value 
    End Set 
End Property 
Private m_Model As String 

Public Function Clone() As Object Implements System.ICloneable.Clone 
    Return New Car() With { _ 
    .Make = Me.Make, _ 
    .Model = Me.Model _ 
    } 
End Function 
End Class 



Public Class CarEqualityComparer 
Implements IEqualityComparer(Of Car) 

Public Overloads Function Equals(ByVal x As Car, ByVal y As Car) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of Car).Equals 
    Return x.Make = y.Make AndAlso x.Model = y.Model 
End Function 

Public Overloads Function GetHashCode(ByVal obj As Car) As Integer Implements System.Collections.Generic.IEqualityComparer(Of Car).GetHashCode 
    Return 1 'http://blogs.msdn.com/b/jaredpar/archive/2008/06/03/making-equality-easier.aspx 
End Function 

End Class 

Public Class CarRepository 
    Private _carComparator As New CarEqualityComparer 

    Private _cars As New ChangeTracker(Of Car)(_carComparator) 

    Public Function GetCars() As IEnumerable(Of Car) 
     'TODO: JET/ADO code here, you would obviously do in a for/while loop 
     Dim dbId1 As Integer = 1 
     Dim make1 As String = "Ford" 
     Dim model1 As String = "Focus" 

     Dim dbId2 As Integer = 2 
     Dim make2 As String = "Hyundai" 
     Dim model2 As String = "Elantra" 

     'TODO: create or update car objects 
     Dim car1 As Car 
     If Not _cars.IsTracking(dbId1) Then 
      car1 = New Car() 
     Else 
      car1 = _cars.GetItem(dbId1) 
     End If 

     car1.Make = make1 
     car1.Model = model1 

     If Not _cars.IsTracking(dbId1) Then 
      _cars.StartTracking(dbId1, car1) 
     End If 


     Dim car2 As Car 
     If Not _cars.IsTracking(dbId2) Then 
      car2 = New Car() 
     Else 
      car2 = _cars.GetItem(dbId2) 
     End If 

     car2.Make = make2 
     car2.Model = model2 

     If Not _cars.IsTracking(dbId2) Then 
      _cars.StartTracking(dbId2, car2) 
     End If 

     Return _cars.GetTrackedItems() 
    End Function 

    Public Sub SaveCars(ByVal cars As IEnumerable(Of Car)) 

     'TODO: JET/ADO code here to update the item 
     Console.WriteLine("Distinct " & cars.Distinct.Count.ToString) 

     For Each changedItem As Car In _cars.GetChangedItems().Intersect(cars) 
      Console.Write("Saving: ") 
      Console.WriteLine(changedItem.Make) 
     Next 

     For Each newItem As Car In cars.Except(_cars.GetTrackedItems()) 
      Console.Write("Adding: ") 
      Console.WriteLine(newItem.Make) 
      Dim newId As Integer = CInt(Math.Ceiling(Rnd() * 5000)) 'Random right now but JET/ADO to get the id later.... 
      _cars.StartTracking(newId, newItem) 
     Next 

     Dim removalArray As New ArrayList 
     For Each deletedItem As Car In _cars.GetTrackedItems().Except(cars) 
      Console.Write("Removing: ") 
      Console.WriteLine(deletedItem.Make) 
      removalArray.Add(_cars.GetId(deletedItem)) 'Cannot remove right as iterating through array - clearly that would be problematic.... 
     Next 
     For Each dbId As Integer In removalArray 
      _cars.StopTracking(dbId) 
     Next 

     _cars.SetNewCheckpoint() 

    End Sub 
End Class 

Public Class ChangeTracker(Of T As {ICloneable}) 
    'item "checkpoints" that are internal to this list 
    Private _originals As New Dictionary(Of Integer, T)() 
    Private _originalIndex As New Dictionary(Of T, Integer)() 

    'the current, live-edited objects 
    Private _copies As New Dictionary(Of Integer, T)() 
    Private _copyIndex As New Dictionary(Of T, Integer)() 

    Private _comparator As System.Collections.Generic.IEqualityComparer(Of T) 

    Public Sub New(ByVal comparator As System.Collections.Generic.IEqualityComparer(Of T)) 
     _comparator = comparator 
    End Sub 

    Public Function IsChanged(ByVal copy As T) As Boolean 
     Dim original = _originals(_copyIndex(copy)) 

     Return Not _comparator.Equals(copy, original) 

    End Function 

    Public Function GetChangedItems() As IEnumerable(Of T) 
     Dim items As IEnumerable(Of T) 
     items = _copies.Values.Where(Function(c) IsChanged(c)) 
     Return items 
    End Function 

    Public Function GetTrackedItems() As IEnumerable(Of T) 
     Return _copies.Values 
    End Function 

    Public Sub SetNewCheckpoint() 
     For Each copy In Me.GetChangedItems().ToList() 
      Dim dbId As Integer = _copyIndex(copy) 
      Dim oldOriginal = _originals(dbId) 
      Dim newOriginal = DirectCast(copy.Clone(), T) 

      _originals(dbId) = newOriginal 
      _originalIndex.Remove(oldOriginal) 
      _originalIndex.Add(newOriginal, dbId) 
     Next 
    End Sub 

    Public Sub StartTracking(ByVal dbId As Integer, ByVal item As T) 
     Dim newOriginal = DirectCast(item.Clone(), T) 
     _originals(dbId) = newOriginal 
     _originalIndex(newOriginal) = dbId 

     _copies(dbId) = item 
     _copyIndex(item) = dbId 
    End Sub 

    Public Sub StopTracking(ByVal dbId As Integer) 
     Dim original = _originals(dbId) 
     Dim copy = _copies(dbId) 

     _copies.Remove(dbId) 
     _originals.Remove(dbId) 
     _copyIndex.Remove(copy) 
     _originalIndex.Remove(original) 
    End Sub 

    Public Function IsTracking(ByVal dbId As Integer) As Boolean 
     Return _originals.ContainsKey(dbId) 
    End Function 

    Public Function IsTracking(ByVal item As T) As Boolean 
     Return _copyIndex.ContainsKey(item) 
    End Function 

    Public Function GetItem(ByVal dbId As Integer) As T 
     Return _copies(dbId) 
    End Function 

    Public Function GetId(ByVal item As T) As Integer 
     Dim dbId As Integer = (_copyIndex(item)) 
     Return dbId 
    End Function 

End Class 

cevap

3

, ben, nerede bir Depo benzeri desen kullanarak öneriyoruz: yorumlarla vb sonra VB.NET içinde

Final kod Depo parçaları, kaydetme işlemlerini gerçekleştirdiğinde değişir.

Bu varlık Framework kendini izleme varlıkları (STE) nasıl uyguladığını benzer. EF STE olarak, bir izleyici nesne o bir nesne 'kirli' olup olmadığını belirlemek amacıyla PropertyChanged benzer olaylara dinler izlemek istediğiniz her varlık için oluşturulur.

Bu yaklaşımın en büyük yararı, Modeller veya GörünümModel'lerinizle herhangi bir kalıcılık durumunu saklamak veya DB'ye olan her şeyi kaydetmek zorunda kalmadan toplu güncelleştirme/silme işlemlerini gerçekleştirebilmenizdir. Bu, kaygıların daha da iyi bir şekilde ayrılmasını sağlar (DAL'ye karşı vs VM vs V). MVVM ve Depozit Kalıbının birlikte çok iyi gittiğini görüyorum. Bir depoyu içinden veritabanından

  1. Sen yük öğeleri:

    İşte genel yaklaşım bu. Eğer öğelerin yüklenmesini gibi, başlangıçta "canlı" (düzenlenemez) nesneye bir ilişki, hem de veritabanında saklanan gibi nesnenin bir kopyasını koruyan "izci" nesne saklayın. Biz buna "kontrol noktası yaratma" diyoruz.

  2. Sen kullanıcı istediği değişiklikleri yapmak için izin zamanki gibi MVVM düzenlenebilir nesneler kullanın. Değişiklikleri izlemeniz gerekmez. kullanıcı 'Kaydet' düğmesini tıkladığında
  3. , geri kaydedilmesini depoya ekrandaki tüm nesneleri göndermek.
  4. Depo kontroller orijinal kopya karşı her nesnenin ve "kirli" olan öğeler belirler.
  5. Sadece kirli ürün veritabanına kaydedilir.
  6. kaydet başarılı olduğunda
  7. , yeni bir kontrol noktası oluşturmak.

    Birincisi, burada bizim depodaki kullanacağı Car adında bir örnek sınıfı var:

benım çırpılmış bazı örnek kod. Nesnede Dirty özelliği olmadığına dikkat edin.

public class Car : IEquatable<Car>, ICloneable 
{ 
    public string Make { get; set; } 
    public string Model { get; set; } 

    public bool Equals(Car other) 
    { 
     return other.Make == this.Make && 
       other.Model == this.Model; 
    } 

    public object Clone() 
    { 
     return new Car { Make = this.Make, Model = this.Model }; 
    } 
} 

Sonraki kullanıcıları, işte veritabanından nesneleri başlatmak için kullanacağı bir CarRepository geçerli:

public class CarRepository 
{ 
    private ChangeTracker<Car> _cars = new ChangeTracker<Car>(); 

    public IEnumerable<Car> GetCars() 
    { 
     //TODO: JET/ADO code here, you would obviously do in a for/while loop 
     int dbId1 = 1; 
     string make1 = "Ford"; 
     string model1 = "Focus"; 

     //TODO: create or update car objects 
     Car car1; 
     if (!_cars.IsTracking(dbId1)) 
      car1 = new Car(); 
     else 
      car1 = _cars.GetItem(dbId1); 

     car1.Make = make1; 
     car1.Model = model1; 

     if (!_cars.IsTracking(dbId1)) 
      _cars.StartTracking(dbId1, car1); 

     return _cars.GetTrackedItems(); 
    } 

    public void SaveCars(IEnumerable<Car> cars) 
    { 
     foreach (var changedItem in _cars.GetChangedItems().Intersect(cars)) 
     { 
      //TODO: JET/ADO code here to update the item 
     } 

     foreach (var newItem in cars.Except(_cars.GetTrackedItems())) 
     { 
      //TODO: JET/ADO code here to add the item to the DB and get its new ID 
      int newId = 5; 
      _cars.StartTracking(newId, newItem); 
     }    

     _cars.SetNewCheckpoint(); 
    } 
} 

Son olarak, Depo değişiklikleri izlemek ve kontrol noktaları kurun ChangeTracker denilen için kullandığı bir yardımcı sınıf var . Burada

public class ChangeTracker<T> where T : IEquatable<T>, ICloneable 
{ 
    //item "checkpoints" that are internal to this list 
    private Dictionary<int, T> _originals = new Dictionary<int, T>(); 
    private Dictionary<T, int> _originalIndex = new Dictionary<T, int>(); 

    //the current, live-edited objects 
    private Dictionary<int, T> _copies = new Dictionary<int, T>(); 
    private Dictionary<T, int> _copyIndex = new Dictionary<T, int>(); 

    public bool IsChanged(T copy) 
    { 
     var original = _originals[_copyIndex[copy]]; 
     return original.Equals(copy); 
    } 

    public IEnumerable<T> GetChangedItems() 
    { 
     return _copies.Values.Where(c => IsChanged(c)); 
    } 

    public IEnumerable<T> GetTrackedItems() 
    { 
     return _copies.Values; 
    } 

    public void SetNewCheckpoint() 
    { 
     foreach (var copy in this.GetChangedItems().ToList()) 
     { 
      int dbId = _copyIndex[copy]; 
      var oldOriginal = _originals[dbId]; 
      var newOriginal = (T)copy.Clone(); 

      _originals[dbId] = newOriginal; 
      _originalIndex.Remove(oldOriginal); 
      _originalIndex.Add(newOriginal, dbId); 
     } 
    } 

    public void StartTracking(int dbId, T item) 
    { 
     var newOriginal = (T)item.Clone(); 
     _originals[dbId] = newOriginal; 
     _originalIndex[newOriginal] = dbId; 

     _copies[dbId] = item; 
     _copyIndex[item] = dbId; 
    } 

    public void StopTracking(int dbId) 
    { 
     var original = _originals[dbId]; 
     var copy = _copies[dbId]; 

     _copies.Remove(dbId); 
     _originals.Remove(dbId); 
     _copyIndex.Remove(copy); 
     _originalIndex.Remove(original); 
    } 

    public bool IsTracking(int dbId) 
    { 
     return _originals.ContainsKey(dbId); 
    } 

    public bool IsTracking(T item) 
    { 
     return _copyIndex.ContainsKey(item); 
    } 

    public T GetItem(int dbId) 
    { 
     return _liveCopies[dbId]; 
    } 
} 

Ve, bir programda da depoyu kullanmak gibi olacaktır:

static void Main(string[] args) 
{ 
    var repository = new CarRepository(); 

    var cars = repository.GetCars().ToArray(); 

    //make some arbitrary changes... 
    cars[0].Make = "Chevy"; 
    cars[1].Model = "Van"; 

    //when we call SaveCars, the repository will detect that 
    //both of these cars have changed, and write them to the database 
    repository.SaveCars(cars); 
} 

bu kesinlikle gerekli değildir ve muhtemelen daha iyi yolları vardır gerçi, IEquatable ve ICloneable dayanan bu naif uygulama bir şeyler yapmanın veya bir öğenin değişip değişmediğini belirlemenin daha etkili bir yoluna sahip olabilirsiniz. (Örneğin, nesne kopyaları oluşturma fikri tam olarak bellek dostu değildir). Ayrıca, silinmiş öğelerle de ilgilenmeniz gerekecek, ancak yukarıdaki örneğe eklemek kolay olacaktır.

+0

Merhaba Kevin, ben sadece yarısını anlıyorum - bu kodda gerçekten neler olduğunu açıklayabilir misiniz? Bir anlatı gibi mi? –

+0

Merhaba Ömer, Birkaç düzenleme yaptım, umarım yardımcı olur. Çok kısa bir versiyon, Kirli izleme yapmakta olduğumuzdur, ancak kullanıcı hangi değişiklikleri yaparsa kirleten bayraklar yönetmek yerine, kullanıcı Kaydet'i tıklattığında hangi nesnelerin kirli olduğunu hesaplamamız dışında. –

+0

Hey Kevin, ovveriding GetHashCode hakkında ne dersiniz? –