Sunday, November 13, 2011

n layer web app with Entity Framework 4.1/4.2 - Things to consider

With Entity Framework 4.1, it releases DbContext API and CodeFirst development model. It is recommended to work with DbContext which exposes the commonly used features of ObjectContext.
Creating object context in DatabaseFirst or ModelFirst approach can be done with the help of ADO.NET DbContext Genrator template in Visual Studio. To achieve that open the .edmx file and right click the base surface >> Add Code Generation Item >> Code >> ADO.NET DbContext Generator. This template is available only when you install EF 4.1 directly. If you install the EF4.1 with NuGet packages, the reference will get added but the ADO.NET DbContext Generator template won't be available. You can use NuGet packages for adding(and managing) the reference to the specific project or solution.

Repository pattern suits best for this case, so that we can create an individual repository for each entity. For details on repository pattern please visit msdn() and Martin Fowler's explanation. Following is a simple and common representation of repository that developers adopt using EF4.1. Please note that following snippet is just to understand the concept.

Snippet 1

public class CompanyRepository: IDisposable
{
    private HSMDBEntities Context { get; set; }

    public CompanyRepository()
    {
        this.Context = new HSMDBEntities();
    }

    public void Create(Company company)
    {
        this.Context.Companies.Add(company);
        this.Context.SaveChanges();
    }

    public Company GetCompanyById(int id)
    {
        //this.Context.Companies.Load();
        //return this.Context.Companies.Where(c => c.Id == id).Single();
        return this.Context.Companies.Find(id);
        
    }

    public IEnumerable<Company> GetAll()
    {
        return this.Context.Companies.ToList();
    }
    
    public void Update(Company company)
    {
        Company currentEntry = this.Context.Companies.Where(c => c.Id == company.Id).First();

        //Client wins model.
        this.Context.Entry(currentEntry).CurrentValues.SetValues(company);
        this.Context.SaveChanges();
    }

    public void Remove(Company company)
    {
        Remove(company.Id);
    }

    public void Remove(int id)
    {
        Company currentEntry = this.Context.Companies.Where(c => c.Id == id).Single();
        this.Context.Companies.Remove(currentEntry);
    }

    public void Dispose()
    {
        this.Context.Dispose();
    }
}

The above Repository snippet exposes the basic operations performed over the entity. Most of the methods are straight forward to understand and the two methods we need to concentrate are GetCompanyById() and Update().

GetCompanyById()  - The DbSet<T> property of the DbContext instance exposes a Find method which uses the primary key to find the entity. The speciality of this method is that the database will be hit only if the entity with the specific key is not found in the context. If the specific entity already got loaded, that entity will be returned upon request without hitting the database. That's why I've commented out the default way of retrieving the entity.

Update() - Consider the following scenario. In n layer architecture, the data model/entity retrieved will be converted into domain specific model and other DTOs(Data Transfer Objects), while passing across the layer to reach the client application(like web app). Similarly the modified data from the client reaches the data tier passing several transitions, and we simply update the database with the values from client. If any other request updates some column value of the entity in the interim time, those changes won't be respected, and whatever value reaches the data tier is updated in a brutal manner. This approach is called as client wins approach which is used common among n layer web applications. I restrict myself ending this update scenario with this short detail as this discussion itself can extend as a separate article. This client wins model is achieved in the above snippet by fetching the current status from the database and overwriting the values got from the client and continue saving it. The highlight here is the SetValues() method of the property DbPropertyValue, which assigns all the current values of individual properties with that of the properties of the parameter object supplied to it. This eradicates the great work of reassigning the individual property values explicitly.

this.Context.Entry(currentEntry).CurrentValues.SetValues(company);


in the place of

var entityEntry = this.Context.Entry(currentEntry);
entityEntry.Property(p => p.Id).CurrentValue = 1;
entityEntry.Property(p => p.CompanyName).CurrentValue = "Company name altered";

 

Now comes the reason for implementing the IDisposable. The best practice regarding the creation of DbContext instance when handling with a web application is - one context instance per request. Hence in n layer web application architecture, components in other layer(most of the times will be Business components) should take the responsibility of handling the lifetime of the context. This can be achieved by handling the respective repository instance with the using construct which automatically calls the dispose method of the repository instance.

using (CompanyRepository companyRepository = new CompanyRepository())
{
...............................
}


In real cases a base and a generic repository will be created based on the project nature and repository  created for each entities will be extended from the base repository. At that situation the base repository class will implement the IDisposable which handles the context instance creation and its lifetime.

public class CompanyRepository : BaseRepository
{        
    public CompanyRepository()
        :base()
    {
        
    }

    public void Create(Company company)
    {
        this.Context.Companies.Add(company);
        this.Context.SaveChanges();
    }

    public Company GetCompanyById(int id)
    {
        //this.Context.Companies.Load();
        //return this.Context.Companies.Where(c => c.Id == id).Single();
        return this.Context.Companies.Find(id);

    }

    public IEnumerable<Company> GetAll()
    {
        return this.Context.Companies.ToList();
    }

    public void Update(Company company)
    {
        Company currentEntry = this.Context.Companies.Where(c => c.Id == company.Id).First();

        //Client wins model.
        this.Context.Entry(currentEntry).CurrentValues.SetValues(company);
        this.Context.SaveChanges();
    }

    public void Remove(Company company)
    {
        Remove(company.Id);
    }

    public void Remove(int id)
    {
        Company currentEntry = this.Context.Companies.Where(c => c.Id == id).Single();
        this.Context.Companies.Remove(currentEntry);
    }

}

public class BaseRepository : IDisposable
{
    protected HSMDBEntities Context { get; set; }

    public BaseRepository()
    {
        this.Context = new HSMDBEntities();
    }

    public void Dispose()
    {
        this.Context.Dispose();
    }
}


The BaseRepository also need to be extended further. Its good to have the above things in mind while developing n layer web application. 

1 comment:

  1. Good post.
    Quick question: let's say you had another entity repository call PersonRepository, how would you handle if you wanted to make method calls to different repository.
    e.g.
    using (CompanyRepository companyRepository = new CompanyRepository())
    {

    PersonRepository pr = new PersonRepository(); //this would instantiate the Context again?
    pr.CallAMethod();
    //or do I wrap this to another using statement?

    }
    In short how would I implement a unit of work here so I only use one connection.

    ReplyDelete

Creative Commons License
This work by Tito is licensed under a Creative Commons Attribution 3.0 Unported License.