Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7645739
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 31, 20262026-05-31T10:01:07+00:00 2026-05-31T10:01:07+00:00

This is more a conceptual question. Here is my current predicament; I am writing

  • 0

This is more a conceptual question. Here is my current predicament; I am writing a vb.net WPF application and using the MVVM pattern (love it! maintainability is just amazingly awesome). Currently all the code is written by hand and there is no use of NHibernate or Entity Framework as the backend is an access database (due to policy I cannot use NH and EF doesn’t support JET Databases, we may switch to MSSQL at some point but that may be a while from now).

The application is running quite well and was wondering what is the best way to send updates back to the database.

Currently the method is to add a boolean to a record on the set portion of the model to “dirty” then when update is pressed we loop through all the records that are “dirty” and use oledbcommand (execute with parameters) sql statements to update.

This create an excellent separation of concerns but if this is the wrong way I would like to know alternatives (please note the database type and the associated drawbacks such is it not working with EF).

Thanks!

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
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-31T10:01:09+00:00Added an answer on May 31, 2026 at 10:01 am

    Since you are using an Update/Save button to commit you changes to the database, I would recommend using a Repository-like pattern, where the Repository tracks changes whenever it performs save operations.

    This is similar to how Entity Framework implements self-tracking entities (STE). In EF STE, a tracker object is created for each entity you want to track that listens to events similar to PropertyChanged in order to determine if an object is ‘dirty’.

    The major benefit to this approach is that you can perform batch update/deletes without needing to store any persistence states with your Models or ViewModels, or having to always save everything you have to the DB. This provides even greater separation of concerns (DAL vs M vs VM vs V). I find that MVVM and the Repository Pattern go very well together.

    Here’s the overall approach:

    1. You load items from the database from within a Repository. As you load items, you store them in a “tracker” object that retains a copy of the object as it was originally stored in the database, as well as a relationship to the “live” (editable) object. We call this process “creating a checkpoint”.
    2. You use the editable objects in your MVVM as usual, allowing the user to make any changes they want. You don’t need to track any changes.
    3. When the user clicks the ‘Save’ button, you send all of the objects on screen back into the repository to be saved.
    4. The Repository checks each object against the original copies and determines which items are “dirty”.
    5. Only the dirty items are saved to the database.
    6. Once the save is successful, you create a new checkpoint.

    Here’s some sample code I whipped up:

    First, here’s a sample class called Car that we will use in our Repository. Notice that there is no Dirty property on the object.

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

    Next, here is a CarRepository that you will use to initialize objects from your database:

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

    Lastly, there is a helper class that the Repository uses to track changes and set checkpoints called 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];
        }
    }
    

    And, here’s how you would use your Repository in a program:

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

    This naive implementation relies on IEquatable and ICloneable, though these are certainly not necessary and there are likely better ways of doing things or you may have a more efficient way of determining whether an item has changed. (For example, the idea of creating object copies is not exactly memory-friendly). You’ll also need to deal with deleted items as well, but that would be easy to add to the sample above.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have an interesting situation here this time. This more of a conceptual question,
This is more like a conceptual question. When to use Model Binding (in ASP.NET
This is more a conceptual question. It's inspired from using some extremely large table
I have an application which I am developing using WPF\Prism\MVVM. All is going well
This is more of a conceptual question than anything. I have a base class
This is more of a conceptual question. If it helps, lets say it falls
This is more of a conceptual question than an actual implementation and am hoping
This is more of a conceptual question concerning the built in functionality of PHP
this is more of a conceptual question. Is it aceptable/good programming mixing do/while loops
So this question is not so much technical but more sort of conceptual. I

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.