-
Notifications
You must be signed in to change notification settings - Fork 1
Using ChangeTracker
Use ChangeTracker as an optional argument with Save*
, Update*
and Merge*
methods to update changed properties only. ChangeTracker<T>
has no effect during inserts. Here's a simple usage. Some context has been omitted to keep the example short:
var emp = await cn.GetAsync<Employee>(id);
var ct = new ChangeTracker<Employee>(emp);
emp.Type = "whatever";
emp.IsExempt = false;
await cn.SaveAsync(emp, ct);
In this example, only the Type
and IsExempt
properties will be affected by the update. I like using this when I want to be sure that updates don't touch columns unnecessarily.
You can go a step further and log ChangeTracker<T>
instances to keep a persistent history of all updates for all users. This requires an additional Nuget package Dapper.CX.ChangeTracking. The next section describes this.
Note: I've retired this package, and am working on new documentation for using the [TrackChanges]
attribute
To log changes in your database to a table, install package Dapper.CX.ChangeTracking, and use LoggedChangeTracker instead of the regular ChangeTracker<T>
. The first time you use LoggedChangeTracker<T>
it will create two tables in your database under a changes
schema: RowVersion and ColumnHistory.
Use LoggedChangeTracker<T>
almost exactly as you would an ordinary ChangeTracker<T>
. The difference is that you must pass a user name that the current changes are done by, and you must use the async crud methods SaveAsync
, UpdateAsync
, or MergeAsync
that accept a ChangeTracker<T>
argument. (Sync methods are not supported.) Adapting the example above from an integration test from the repo, it would look like this for the user adamo
:
var emp = new Employee()
{
LastName = "Herbert",
FirstName = "Yavad",
HireDate = new DateTime(1990, 1, 1),
IsExempt = true,
Status = Status.Active
};
await cn.SaveAsync(emp);
var ct = new LoggedChangeTracker<Employee>("adamo", emp);
emp.FirstName = "Javad";
emp.Status = Status.Inactive;
await cn.SaveAsync(emp, ct);
You can then query the changes.ColumnHistory
table, and it would look like this:
For key columns that you want to convert to text when changes are logged, implement interface ITextLookup on your model class. See example here. The integration test shows how the Price
and TypeId
properties of a model are updated. TypeId
is a key column that is translated to a text value when changes are logged. After running the test, you see that the TypeId
shows the text values this
and other
instead of the numeric Ids. A change in the Price
property is also logged.
var ids = new HashSet<int>();
Array.ForEach(new[] { "this", "that", "other" }, (name) =>
{
ids.Add(await cn.SaveAsync(new WidgetType() { Name = name }));
});
var w = new Widget()
{
Description = "this new thing",
TypeId = ids.First(),
Price = 23.4m
};
int widgetId = await cn.SaveAsync(cn, w);
var ct = new LoggedChangeTracker<Widget>("adamo", w);
w.Price = 21.7m;
w.TypeId = ids.Last();
await cn.SaveAsync(w, ct);