2012-03-14 16 views
5

के लिए एमवीवीएम पैटर्न में रिकॉर्ड अपडेट करने का उचित तरीका यह एक और वैचारिक प्रश्न है। यहां मेरी वर्तमान स्थिति है; मैं एक vb.net WPF अनुप्रयोग लिख रहा हूं और एमवीवीएम पैटर्न का उपयोग कर रहा हूं (इसे प्यार करता हूं! रखरखाव सिर्फ आश्चर्यजनक रूप से अद्भुत है)। वर्तमान में सभी कोड हाथ से लिखे गए हैं और एनएचबीरनेट या एंटिटी फ्रेमवर्क का कोई उपयोग नहीं है क्योंकि बैकएंड एक एक्सेस डेटाबेस है (पॉलिसी के कारण मैं एनएच और ईएफ का उपयोग नहीं कर सकता जेईटी डाटाबेस का समर्थन नहीं करता है, हम किसी बिंदु पर एमएसएसक्यूएल पर स्विच कर सकते हैं लेकिन अब से कुछ समय हो सकता है)।अधिकतम effeciency

एप्लिकेशन बहुत अच्छी तरह से चल रहा है और यह सोच रहा था कि डेटाबेस में अपडेट भेजने का सबसे अच्छा तरीका क्या है।

वर्तमान में विधि "गंदे" के मॉडल के सेट हिस्से पर एक रिकॉर्ड में एक बूलियन जोड़ने के लिए है, तो जब अद्यतन दबाया जाता है तो हम "गंदे" वाले सभी रिकॉर्ड्स के माध्यम से लूप करते हैं और oledbcommand (पैरामीटर के साथ निष्पादित) का उपयोग करते हैं। अद्यतन करने के लिए एसक्यूएल कथन।

यह चिंताओं का एक उत्कृष्ट अलगाव पैदा करता है लेकिन यदि यह गलत तरीका है तो मैं विकल्पों को जानना चाहता हूं (कृपया डेटाबेस प्रकार और संबंधित दोषों को नोट करें जैसे यह ईएफ के साथ काम नहीं कर रहा है)।

धन्यवाद!

VB.NET में अंतिम कोड टिप्पणी आदि के बाद:

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 

उत्तर

3

चूंकि आप एक अद्यतन/सहेजें बटन का उपयोग कर रहे आप डेटाबेस में परिवर्तन के लिए प्रतिबद्ध करने, मैं, जहां एक भंडार की तरह पद्धति का उपयोग कर की सिफारिश करेंगे जब भी यह संचालन को बचाता है तो रिपोजिटरी ट्रैक बदलता है।

यह इसी प्रकार है कि एंटीटी फ्रेमवर्क स्वयं ट्रैकिंग इकाइयों (एसटीई) को कैसे लागू करता है। ईएफ एसटीई में, प्रत्येक इकाई के लिए एक ट्रैकर ऑब्जेक्ट बनाया जाता है जिसे आप ट्रैक करना चाहते हैं, यह निर्धारित करने के लिए कि कोई ऑब्जेक्ट 'गंदे' है या नहीं, यह निर्धारित करने के लिए PropertyChanged जैसी घटनाओं को सुनता है।

इस दृष्टिकोण का मुख्य लाभ यह है कि आप अपने मॉडल या व्यू मॉडल्स के साथ किसी भी दृढ़ता वाले राज्यों को स्टोर करने की आवश्यकता के बिना बैच अपडेट/डिलीट कर सकते हैं, या हमेशा आपके पास डीबी में जो कुछ भी है उसे सहेज सकते हैं। यह चिंताओं का भी अधिक अलगाव प्रदान करता है (डीएएल बनाम एम बनाम वीएम बनाम वी)। मुझे लगता है कि एमवीवीएम और रिपोजिटरी पैटर्न एक साथ बहुत अच्छी तरह से जाते हैं।

  1. आप लोड एक भंडार के भीतर से डेटाबेस से आइटम:

    यहाँ समग्र दृष्टिकोण है। जैसे ही आप आइटम लोड करते हैं, आप उन्हें "ट्रैकर" ऑब्जेक्ट में संग्रहीत करते हैं जो ऑब्जेक्ट की प्रतिलिपि बनाए रखता है क्योंकि यह मूल रूप से डेटाबेस में संग्रहीत होता था, साथ ही साथ "लाइव" (संपादन योग्य) ऑब्जेक्ट का संबंध भी था। हम इस प्रक्रिया को "चेकपॉइंट बनाना" कहते हैं।

  2. आप सामान्य रूप से अपने एमवीवीएम में संपादन योग्य ऑब्जेक्ट्स का उपयोग करते हैं, जिससे उपयोगकर्ता इच्छित परिवर्तन करने की अनुमति देता है। आपको किसी भी बदलाव को ट्रैक करने की आवश्यकता नहीं है।
  3. जब उपयोगकर्ता 'सहेजें' बटन पर क्लिक करता है, तो आप सहेजे जाने के लिए स्क्रीन पर सभी ऑब्जेक्ट्स को रिपोजिटरी में वापस भेजते हैं।
  4. रिपोजिटरी प्रत्येक ऑब्जेक्ट को मूल प्रतियों के विरुद्ध जांचता है और यह निर्धारित करता है कि कौन से आइटम "गंदे" हैं।
  5. केवल गंदे आइटम डेटाबेस में सहेजे गए हैं।
  6. एक बार सहेजने के बाद, आप एक नया चेकपॉइंट बनाते हैं।

    सबसे पहले, यहाँ एक नमूना वर्ग Car कहा जाता है कि हम अपने भंडार में उपयोग करता रहेगा:

यहां कुछ नमूना कोड मैं मार पड़ी है। ध्यान दें कि वस्तु पर कोई गंदा संपत्ति नहीं है।

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

इसके बाद, यहाँ एक CarRepository है कि आप अपने डेटाबेस से वस्तुओं प्रारंभ करने में प्रयोग करेंगे:

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(); 
    } 
} 

अन्त में, वहाँ एक सहायक वर्ग है कि भंडार परिवर्तनों को ट्रैक करने और सेट चौकियों ChangeTracker कहा जाता है का उपयोग करता है ।

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

और, आपके द्वारा किसी प्रोग्राम में अपनी भंडार का प्रयोग करेंगे है:

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

यह अनुभवहीन कार्यान्वयन IEquatable और ICloneable पर निर्भर करता है, हालांकि इन निश्चित रूप से आवश्यक नहीं हैं और वहाँ की संभावना बेहतर तरीके हैं चीजों को करने या आपके पास यह निर्धारित करने का एक और अधिक प्रभावी तरीका हो सकता है कि कोई आइटम बदल गया है या नहीं। (उदाहरण के लिए, ऑब्जेक्ट प्रतियां बनाने का विचार बिल्कुल स्मृति-अनुकूल नहीं है)। आपको हटाए गए आइटमों के साथ भी निपटने की आवश्यकता होगी, लेकिन ऊपर दिए गए नमूने में जोड़ना आसान होगा।

+0

हाय केविन, मैं केवल आधा समझ रहा हूं - क्या आप समझ सकते हैं कि वास्तव में उस कोड में क्या हो रहा है? एक कथा की तरह? –

+0

हाय उमर, मैंने कुछ संपादन किए हैं, मुझे आशा है कि यह सहायक होगा। बहुत छोटा संस्करण यह है कि हम अभी भी गंदा ट्रैकिंग कर रहे हैं, सिवाय इसके कि हम गणना करते हैं कि उपयोगकर्ता गंदे झंडे के प्रबंधन के बजाय गंदे झंडे के प्रबंधन के बजाय गंदे हैं, जब उपयोगकर्ता परिवर्तन करता है। –

+0

हे केविन, GetHashCode ovveriding के बारे में क्या? –

संबंधित मुद्दे