Alexander Beletsky's development blog

My profession is engineering

DDD, Implementation of Repository pattern with Linq to SQL

Lets review first and one of major DDD pattern - Repository. Repository is something that provides access to an entities objects (domain objects). Here what DDD patterns guide says on Repositories:

For each type of object that needs global access, create an object that can provide the illusion of an in-memory collection of all objects of that type. Set up access through a well-known global interface. Provide methods to add and remove objects, which will encapsulate the actual insertion or removal of data in the data store. Provide methods that select objects based on some criteria and return fully instantiated objects or collections of objects whose attribute values meet the criteria, thereby encapsulating the actual storage and query technology. Provide repositories only for aggregate roots that actually need direct access. Keep the client focused on the model, delegating all object storage and access to the repositories.

Preparation


Suppose we are having a database with table Users in it. First of all we have to create a Data Model class. To do that you can add “New item” to you class library (or application) and select Linq to Classes.



This will add new .dbml file your project. There are 2 ways of addition new entities to model. Either by designer (easy to use) or manually by coding .dbml file (harder to user by more flexibility). We will just use first way as more quick one. Just open you db in Server explorer and drag-and-drop Users table to .dbml designer. You will see something like that,



Now if you open corresponding .cs file of Data Model you can easily see what code is actually generated by Linq to SQL. Let’s quickly review it. So, most improtant class is DataContext.

  [global::System.Data.Linq.Mapping.DatabaseAttribute(Name="trackyourtasksdb")]
  public partial class TrackYourTasksDataContext : System.Data.Linq.DataContext
  {
    
    private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource();

* This source code was highlighted with Source Code Highlighter.


DataContext is something that suppose provide access to tables and objects and track the state of these objects. It uses SubmitChanges() method to flush the data to database. It is lightweight object and you should instantiate in in method of class scope, there are no reasons to keep a single copy of object for whole application. It is not only possible to work with tables within DataContext, but stored procedures as well.

Besides of that the object represenation of table record is generated. The class is called User (please not that Linq to SQL is removing plural -s suffix by itself).

  [global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Users")]
  public partial class User : INotifyPropertyChanging, INotifyPropertyChanged
  {
    
    private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
    
    private int _Id;
    
    private string _Email;
    
    private string _SecretPhrase;
    
    private string _Password;
    
    private System.Data.Linq.Binary _Timestamp;
        
        //...


    public User()
    {
      OnCreated();
    }
    
    [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Id", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true, UpdateCheck=UpdateCheck.Never)]
    protected int Id
    {
      get
      {
        return this._Id;
      }
      set
      {
        if ((this._Id != value))
        {
          this.OnIdChanging(value);
          this.SendPropertyChanging();
          this._Id = value;
          this.SendPropertyChanged("Id");
          this.OnIdChanged();
        }
      }
    }
    
    [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Email", DbType="NVarChar(MAX) NOT NULL", CanBeNull=false, IsPrimaryKey=true, UpdateCheck=UpdateCheck.Never)]
    public string Email
    {
      get
      {
        return this._Email;
      }
      set
      {
        if ((this._Email != value))
        {
          this.OnEmailChanging(value);
          this.SendPropertyChanging();
          this._Email = value;
          this.SendPropertyChanged("Email");
          this.OnEmailChanged();
        }
      }
    }

        //...


* This source code was highlighted with Source Code Highlighter.



So as you see it incapsulates fields of table as corresponding properties of object.

Interface


As it said above Repository have to add, remove as well as search and update objects. Note, that we work work with db record (entities classes) as we just work with in-memory objects.

It is always good idea to work with such structures as Repositories, Factories, Services etc. throught interface. First of all, because it conforms to common object oriented design guidlines and make us possibility to have different implementation classes that would provide different behavior. Second, that with interfaces you can substitute real object with mock object during testing. Moreover with interfaces it is easy to use Dependency Injection pattern, that improves application flexability and testability.

User repository interface looks like,

using System;
using System.Linq;
using System.Text;
using Trackyourtasks.Core.DAL.DataModel;

namespace Trackyourtasks.Core.DAL
{
  public interface IUsersRepository
  {
    User FindUserById(int id);
    User FindUserByEmail(string email);

    void SaveUser(User user);
    void DeleteUser(User user);
  }
}

* This source code was highlighted with Source Code Highlighter.


Now let’s implement this repository with Linq to SQL tools. We would review all methods, one by one.

Construction of Repository:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Trackyourtasks.Core.DAL.DataModel;

namespace Trackyourtasks.Core.DAL
{
  public class UsersRepository : IUsersRepository
  {
    TrackYourTasksDataContext _context = new TrackYourTasksDataContext();

* This source code was highlighted with Source Code Highlighter.


As you see I instantiate DataContext object as a member of UsersRepository class. So, it will be alive and tracking objects since UserRepository class is alive.

Find methods:

    public User FindUserById(int id)
    {
      return (from user in _context.Users where user.Id == id select user).SingleOrDefault();
    }

    public User FindUserByEmail(string email)
    {
      return (from user in _context.Users where user.Email == email select user).SingleOrDefault();
    }

* This source code was highlighted with Source Code Highlighter.


Impelementation is just a simple Linq query that selects object either by Id or Email. I use SingleOrDefault() that returns object if found and default if not (null in my case).

Save and Update methods:

    public void SaveUser(User user)
    {
      if (user.Id == 0)
      {
        _context.Users.InsertOnSubmit(user);
      }
      _context.SubmitChanges();
    }

* This source code was highlighted with Source Code Highlighter.


So, if are created user entiry by new operator it will contain Id == 0 (meaning no Id, yet), as we passing it to Save method it will check is this object persistant or not and if not it will be add it to storage. Since context object is not disposed after, it still continue track object changes, so if we decieded to change some object fields and store it all we need to do is call SubmitChanges() method and corrsponding record will be updated. I’ll put some tests here to see how it works:

    [Test]
    public void InsertUser()
    {
      //INIT
      var register = new UsersRepository();

      //ACT
      var user = new User()
      {
        Email = "email",
        SecretPhrase = "sec",
        Password = "pass"
      };

      register.SaveUser(user);

      //POST
      var actual = register.FindUserByEmail("email");
      Assert.That(actual, Is.Not.Null);
    }

    [Test]
    [ExpectedException(typeof(DuplicateKeyException))]
    public void InsertUserTwice()
    {
      //INIT
      var register = new UsersRepository();

      //ACT / POST
      var user = new User()
      {
        Email = "email",
        SecretPhrase = "sec",
        Password = "pass"
      };

      register.SaveUser(user);

      var newUser = new User()
      {
        Email = "email",
        SecretPhrase = "sec",
        Password = "pass"
      };

      register.SaveUser(newUser);
    }

    [Test]
    public void UpdateUser()
    {
      //INIT
      var register = new UsersRepository();

      var user = new User()
      {
        Email = "email",
        SecretPhrase = "sec",
        Password = "pass"
      };

      register.SaveUser(user);

      //ACT
      user.SecretPhrase = "newsec";
      register.SaveUser(user);

      //POST
      var foundUser = register.FindUserById(user.Id);
      Assert.That(foundUser, Is.Not.Null);
      Assert.That(foundUser.SecretPhrase, Is.EqualTo("newsec"));
    }

* This source code was highlighted with Source Code Highlighter.


Delete Methods:

    public void DeleteUser(User user)
    {
      _context.Users.DeleteOnSubmit(user);
      _context.SubmitChanges();
    }

* This source code was highlighted with Source Code Highlighter.


So object is marked as to be deleted by calling DeleteOnSubmit() and it will be actually deleted on SubmitChanges() call.

Conclusions



It is really easy to start up with Repositories keeping its implementation as simple as possible. Of cause, typicaly data structure in databases are bit more complex than plain table. But it is still possible to work with this pattern, combining with Repositories, Aggregates, Entities and Values.