Thursday, June 24, 2010

All kinds of Repository implementations

I've been looking back and reviewing the approaches on some of my previous projects. In particular, I've been going back to review a project I did in 2005. At that point, I didn't really have the concepts of domain model and repository clear: for data access I was using POJOs, but loading them internally, within each POJO, via a private method (which called to stored procedures with parameters using SqlDataReader.)

In all projects since, I've used  a Domain Model and Repository, typically with NHibernate for the ORM mapping/queries.

As I started using Rhino Mocks, I also created mock repositories for MVC and MVP unit testing. And I also have created fake repositories (returning collections of dummy objects), and LinqToSql repositories as part of the PRO ASP.NET MVC book examples [Sanderson].

The only combination I hadn't done was original ADO.NET/SqlDataReader in a repository.

In the following code sample, I add an ADO.NET repository to an Model-View-Presenter scenario (which originally had only mocked repository and view interfaces against a Presenter). To make a complete integration test:

1) I create a view interface implementation (WinForm) where the List<Project> setter property assigned its value to a simple data grid
2) I create an AdoNetProjectsRepository implementation class where List<Project> FindAllProjects() method returns a list of Projects from the database.

Integration test with Form1.cs and ADO.NET Repository

[TestFixture]
public class ProjectsPresenterIntegrationTests
{
    [Test]
    public void ShowProjectsEvent_Raised_RetrievesProjectsFromRepositoryAndAssignedToViewProjectsDisplay()
    {
        IProjectsRepository adoNetProjectsRepository = new AdoNetProjectsRepository();
        var projects = adoNetProjectsRepository.FindAllProjects();

        IProjectsView projectsView = new Form1();
        projectsView.ProjectsDisplay.ShouldEqual(projects);
    }
}


IProjectsView implementation as WinForm

public Form1()
{
    InitializeComponent();
    IProjectsRepository adoNetProjectsRepository = new AdoNetProjectsRepository();
    var projectsPresenter = new ProjectsPresenter(adoNetProjectsRepository, this);
    ShowProjects(this, EventArgs.Empty);
}

public event EventHandler ShowProjects;

public List<Project> ProjectsDisplay
{
    get
    {
        return (List<Project>)dgrProjects.DataSource;
    }
    set
    {
        dgrProjects.DataSource = value;
    }
}


Projects Presenter

public class ProjectsPresenter
{
    private readonly IProjectsRepository _projectsRepository;
    private readonly IProjectsView _projectsView;

    public ProjectsPresenter(IProjectsRepository projectsRepository, IProjectsView projectsView)
    {
        _projectsRepository = projectsRepository;
        _projectsView = projectsView;
        _projectsView.ShowProjects += new EventHandler(ProjectsViewShowProjects);
    }

    private void ProjectsViewShowProjects(object sender, EventArgs e)
    {
        _projectsView.ProjectsDisplay = _projectsRepository.FindAllProjects();
    }
}


ADO.NET Repository

public class AdoNetProjectsRepository : IProjectsRepository
{
    public List<Project> FindAllProjects()
    {
        var projects = new List<Project>();

        PopulateProjectsWithSqlDataReader(projects);

        return projects;
    }

    private static void PopulateProjectsWithSqlDataReader(ICollection<Project> projects)
    {
        SqlDataReader sqlDataReader = null;

        try
        {
            var sqlConnection = new SqlConnection(DBManager.DBConnection);
            var sqlCommand = sqlConnection.CreateCommand();

            sqlCommand.CommandText = DBManager.DBOwner + "sp_getCurrentProjects";
            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlConnection.Open();
            sqlDataReader = sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);

            if (sqlDataReader == null) return;

            while (sqlDataReader.Read())
            {
                var myProject = new Project
                {
                    ProjectId = Convert.ToInt32(sqlDataReader["ProjectID"]),
                    Name = sqlDataReader["Name"].ToString(),
                    ProductName = sqlDataReader["ProductName"].ToString()
                };

                projects.Add(myProject);
            }
        }
        finally
        {
            if (sqlDataReader != null) sqlDataReader.Close();
        }
    }
}


The conclusion I drew from this experiment: while I am accustomed to (and may prefer) to use an ORM tool like NHibernate to do my query retrievals in a repository, it is by no means my only option. Even plain old ADO.NET SqlDataReader can do the trick.

The primary goal is not which data access implementation to use, but instead the abstraction of the Domain Model, Repository, and application services (controller/presenter/web service).

No comments: