This is second part of a series. Check out the first part if you haven’t already. The code can be found at BitBucket repo.
Last time we looked at data retrieval using Active Record –pattern. In this part we’ll do data persistence and do little bit of refactoring.
Saving and deleting
If we have made changes to a record, or created a new one, calling Save()-method persists changes to the data store.
public void Save()
{
using (var ctx = new DataClassesDataContext())
{
var task = ctx.TaskTables.FirstOrDefault(x => x.id == this.id);
//create new record if id is not found
if (task == null)
{
task = new TaskTable();
ctx.TaskTables.InsertOnSubmit(task);
}
task.title = this.title;
task.content = this.content;
task.completed = this.completed;
ctx.SubmitChanges();
this.id = task.id;
}
}
Unlike data retrieval methods, Save() doesn’t need to be declared static, because it is only called on an instance of an object. It handles both update and creation. Line 6 uses lambda expression to find existing record in the data store. If you’re not familiar with lambda expression, you can find more about it here, for example.
Delete() is very straightforward.
public void Delete()
{
using (var ctx = new DataClassesDataContext())
{
ctx.TaskTables.DeleteOnSubmit(
ctx.TaskTables.SingleOrDefault(x => x.id == this.id));
ctx.SubmitChanges();
}
}
You should note that this only removes the row from the database, the active record -object still exists.
Refactoring
As we saw in the last part, our two data retrieval methods share quite a bit of code. The difference was actually just one line. This is not very good. It means that if we have to make change to the way we retrieve data, we would have to remember make same changes in two different places. If we want to add new query capabilities like FetchByTitle, we would have to duplicate code for that too. In order to fixing that duplication, we refactor the class and isolate common functionality.
Isolation of common functionality is achieved with QueryTasks()-method displayed below.
private static List<Task> QueryTasks(Expression<Func<Task,bool>> predicate)
{
var taskList = new List<Task>();
using (var ctx = new DataClassesDataContext())
{
var query = from task in ctx.TaskTables
select new Task
{
id = task.id,
title = task.title,
content = task.content,
completed=task.completed
};
query.Where(predicate);
taskList = query.ToList();
}
return taskList;
}
It is declared private, since it is only going to be used internally by the class. The method looks quite familiar, but this method takes a lambda expression as parameter and applies it as where-clause of the query (line 14). Our data retrieval methods can now be reduced to this:
public static List<Task> FetchAll()
{
return QueryTasks(x => true);
}
public static Task FetchByID(int id)
{
return QueryTasks(x => x.id == id).FirstOrDefault();
}
Conclusion
At this point we have basic implementation for data access based on active record pattern. Active record can be a nice and simple way to access data without adding too many abstraction layers in an application. Active record introduces tight coupling between data model and object model. Sometimes that is not what you want and you may need to look into alternative data access patterns such as Data Mapper.
Software professional with a passion for quality. Likes TDD and working in agile teams. Has worked with wide-range of technologies, backend and frontend, from C++ to Javascript. Currently very interested in functional programming.