The right way to update entries in the MVVM template for maximum efficiency

This is a more conceptual question. Here is my current predicament; I am writing a WPF vb.net application and using the MVVM template (love it, repairability is just amazing amazing). Currently, all the code is written by hand, and there is no need to use NHibernate or Entity Framework, since the backend is an access database (due to the policy I cannot use NH and EF does not support JET databases, we can switch to MSSQL but it may be from time to time).

The application works well enough and wonders how best to send updates back to the database.

Currently, the method consists in adding a logical value to the record on a given part of the model to “dirty”, and then when we click on the update we will skip all the records that are “dirty” and use the oledb command (execute with parameters). sql to update.

This creates an excellent separation of problems, but if this is the wrong way, I would like to know the alternatives (pay attention to the type of database and its associated flaws, for example, this does not work with EF).

Thank!

Final code in VB.NET after comments, etc .:

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
+5
source share
1 answer

"/", , , , , .

, Entity Framework (STE). EF STE , , , PropertyChanged, , "".

, / - ViewModels , , . (DAL vs M vs VM vs V). , MVVM .

:

  • . , "", , , "" () . " ".
  • MVVM, , , . - .
  • "", , .
  • , "".
  • .
  • , .

, :

-, Car, . , Dirty.

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, , , , , , -, , . (, ). , .

+3

All Articles