0

I have a C# program that loads a list of products from a database into a list of Product objects. The user can add new products, edit products, and delete products through my program's interface. Pretty standard stuff. My question relates to tracking those changes and saving them back to the database. Before I get to the details, I know that using something like Entity Framework or NHiberate would solve my problem about tracking adds and edits, but I don't think it would solve my problem about tracking deletes. In addition to wanting an alternative to converting a large codebase to using Entity Framework or NHiberate, I also want to know that answer to this question for my own curiosity.

In order to track edits, I'm doing something like this on the Product class where I set the IsDirty flag any time a property is changed:

class Product
{
   public bool IsDirty { get; set; }
   public bool IsNew { get; set; }

   // If the description is changed, set the IsDirty property
   public string Description
   { 
      get
      {
         return _description;
      }
      set
      {
         if (value != _description)
         {
            this.IsDirty = true;
            _description = value;
         }         
      }
   }
   private string _description;

   // ... and so on
}

When I create a new Product object, I set its IsNew flag, so the program knows to write it to the database the next time the user saves. Once I write a product to the database successfully, I clear its IsNew and IsDirty flags.

In order to track deletes, I made a List class that tracks deleted items:

class EntityList<T> : List<T>
{
   public List<T> DeletedItems { get; private set; }

   EntityList()
   {
      this.DeletedItems = new List<T>();
   }

   // When an item is removed, track it in the DeletedItems list
   public new bool Remove(T item)
   {
      this.DeletedItems.Add(item);
      base.Remove(item);
   }
}

// ...

// When I work with a list of products, I use an EntityList instead of a List
EntityList<Product> products = myRepository.SelectProducts();

Each time I save a list of products to the database, I iterate through all of the products in the EntityList.DeletedItems property and delete those products from the database. Once the list is saved successfully, I clear the DeletedItems list.

All of this works, but it seems like I may be doing too much work, especially to track deleted items and to remember to set the IsNew flag every time I create a new Product object. I can't set the IsNew flag in Product's constructor because I don't want that flag set if I'm loading a Product object from the database. I'm also not thrilled with the fact that I have to use my EntityList class everywhere instead of using List.

It seems like this scenario is extremely common, but I haven't been able to find an elegant way of doing it through my research. So I have two questions:

1) Assuming that I'm not using something like Entity Framework, is there a better way to track adds, edits, and deletes and then persist those changes to the database?

2) Am I correct in saying that even when using Entity Framework or NHiberate, that I'd still have to write some additional code to track my deleted items?

Ben Rubin
  • 6,909
  • 7
  • 35
  • 82

2 Answers2

0

In EF the DbContext object contains all of the logic to track changes to objects that it knows about. When you can SaveChanges it figures out which changes have happened and performs the appropriate actions to commit those changes to the database. You don't need to do anything specific with your object state other than inform the DbContext when you want to add or remove records.

Updates:

When you query a DbSet the objects you get are tracked internally by EF. During SaveChanges the current state of those objects are compared against their original state and those that are changed are put into a queue to be updated in the data.

Inserts:

When you add a new object to the relevant DbSet it is flagged for insertion during the SaveChanges call. The object is enrolled in the change tracking, it's DB-generated fields (auto-increment IDs for instance) are updated, etc.

Deletes:

To delete a record from the database you call Remove on the relevant DbSet and EF will perform that action during the next SaveChanges call.

So you don't need to worry about tracking those changes for the sake of the database, it's all handled for you. You might need to know for your own benefits - it's sometimes nice to be able to color changed records for instance.

The above is also true for Linq2SQL and probably other ORMs and database interface layers, since their main purpose is to allow you to access data without having to write reams of code for doing things that can be abstracted out.

Corey
  • 15,524
  • 2
  • 35
  • 68
0
  1. is there a better way to track adds, edits, and deletes and then persist those changes to the database?

Both Entity Framework and NHibernate chose not to make entities themselves responsible for notifying nor tracking their changes*. So this can't be a bad choice. It certainly is a good choice from a design pattern's point of view (single responsibility).

They store snapshots of the data as they are loaded from the database in the context or session, respectively. Also, these snapshots have states telling whether they are new, updated, deleted or unchanged. And there are processes to compare actual values and the snapshots and update the entity states. When it's time to save changes, the states are evaluated and appropriate CRUD statements are generated.

This is all pretty complex to implement all by yourself. And I didn't even mention integrity of entity states and their mutual associations. But of course it's doable, once you decide to follow the same pattern. The advantage of the data layer notifying/tracking changes (and not the entities themselves) is that the DAL know which changes are relevant for the data store. Not all properties are mapped to database tables, but the entities don't know that.

  1. I'd still have to write some additional code to track my deleted items?

No. Both OR mappers have a concept of persistence ignorance. You basically just work with objects in memory, which may encompass removing them from a list (either nested in an owner entity or a list representing a database table) and the ORM knows how to sync the in-memory state of the entities with the database.


*Entity Framework used to have self-tracking entities, but they were deprecated.

Community
  • 1
  • 1
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • Since Entity Framework is storing snapshots of my data, how much overhead does that incur? Does it essentially store each object twice (the current working copy and the original copy), thus doubling the amount of memory used? – Ben Rubin Jul 19 '15 at 00:34
  • It stores the copy as a list of values and the working copy is the object that's materialized and that you work with. Is this overhead? It's an indispensable part of the whole process. Anyway, it's always recommended to work with short-lived contexts (or sessions), otherwise they become very heavy and slow. – Gert Arnold Jul 19 '15 at 07:18