Introduction
In this application, I will explain how we can build our Models and Data layer using Microsoft best practices.
Our road map is to:
- Create Models POCO
- Create
DBContext
from Entity Framework
- Create Repositories (Pattern)
- Create UOW (Unit Of Work pattern)
- POCO Models will act as data carriers and they are independent (stand alone). These classes don't know anything about the DB.
- Entity framework is an ORM (object-relational mapper) that enables us to connect to the DB (SQL server) and map DB to our models and vice versa. We can consider it as a conduit.
- Repository pattern will expose the data retrieval, updating and saving of the data to our DB by communicating with EF (Entity Framework), and to simplify and standardize the way we work with retrieval, updating and saving.
- Unit of work pattern is to add all our repositories in one place, so we can do commit and unroll multiple changes to our repositories.
But why do we want to use all those patterns technologies? Well, the simple answer is reusability and maintainability, by decoupling the classes from their behaviour and following the single responsibility principle.
Figure 1
Using the Code
Create Models POCO
We will start with models, easy and simple.
First, we will add new class library project into our solution called Model
as shown in (Figure 2).
Figure 2
Models are the data carriers like we said and illustrated in (Figure 1), those Models will be classes, simple classes only contain properties, no attributes describing infrastructure concerns or other responsibilities that your domain objects shouldn't have (independent) and we call them POCO. They are Plain Old CLR Objects (POCO) .
For our application, we will create three classes (Applicant
, Certification
, Skill
) and add those classes to the Model Project that we have created.
Let's take a look at those classes:
public class Applicant
{
public Applicant()
{
Certifications = new List<Certification>();
Skills = new List<Skill>();
}
[Key]
public int ApplicantID { get; set; }
public string First_Name { get; set; }
public string Last_Name { get; set; }
public string Email { get; set; }
public string Tel_No { get; set; }
public string Mobile_No { get; set; }
public string Visa_Type { get; set; }
public string LinkedIn_URL { get; set; }
public string Objective { get; set; }
public byte Active { get; set; }
public virtual ICollection<Certification> Certifications { get; set; }
public virtual ICollection<Skill> Skills { get; set; }
}
public class Certification
{
[Key]
public int id { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public Nullable<System.DateTime> Expire_Date { get; set; }
public string Description { get; set; }
public int ApplicantID { get; set; }
public byte Active { get; set; }
public virtual Applicant Applicant { get; set; }
}
public class Skill
{
[Key]
public int id { get; set; }
public string Description { get; set; }
public int ApplicantID { get; set; }
public byte Active { get; set; }
public virtual Applicant Applicant { get; set; }
}
Those are just simple classes, but if you notice, you will see that we have key attribute, which comes from (using System.ComponentModel.DataAnnotations;
). It's just to indicate that the id is the primary key for the entity framework, but the entity framework is smart enough to know that property with id name is the primary key without adding the [key]
attribute.
Create DBContext from Entity Framework
First, we will add new class library project into our solution called Data
as shown in (Figure 3).
Figure 3
Second, we will add a reference to Model Project, as shown in (Figure 4).
Figure 4
Finally, we will add entity framework from Manage NuGet packages, we can go to the Data Project in solution explorer, right click and choose Manage NuGet Packages as shown in (Figure 5).
Figure 5
Then, search for entity framework in the search box and choose entity framework from Microsoft and click install, as shown in (Figure 6).
Figure 6
Now, we can start to build our DbContext
class.
DbContext
comes from Entity framework which allows us to interact between our database and our Models. It basically defines the relations between Models and the DB, personally I call the DbContext
the conduit.
In the Dbcontext
, we will have DbSet<T>
which defines the relation between Tables and Models. When we go to our example, the DbContext
will contain public DbSet<Applicant> Applicants { get; set; }
, consider DbSet
as the glue that will tie our models with table in DB.
Another thing we will have inside our DbContext
is Configurations
, which is responsible for configuring Entity(Tables) relations in DB.
Here is the DbContext
class:
public partial class MyExperienceDBContext : DbContext
{
public MyInfoDBContext()
: base("Name=MyExperience")
{
}
public DbSet<Applicant> Applicants { get; set; }
public DbSet<Certification> Certifications { get; set; }
public DbSet<Skill> Skills { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ApplicantConfig());
modelBuilder.Configurations.Add(new CertificationConfig());
modelBuilder.Configurations.Add(new SkillConfig());
}
}
I called the my DbContext
class MyExperienceDBContext
and this class inherits from DbContext
which is part of the entity framework, in the constructor, I called the DbContext
class constructor and passed the connection string name ("Name=MyExperience
"), this connection string will exist of course inside the configuration file in your application.
As you can see from the OnModelCreating
method, we are pointing to ApplicantConfig
, CertificationConfig
and SkillConfig
classes which we will create, and please note that those classes normally contain the relations configuration
and Column
to model property .
Here are the configuration classes:
public class SkillConfig : EntityTypeConfiguration<Skill>
{
public SkillConfig()
{
this.HasRequired(t => t.Applicant)
.WithMany(g => g.Skills)
.HasForeignKey(d => d.ApplicantID);
}
}
public class CertificationConfig : EntityTypeConfiguration<Certification>
{
public CertificationConfig()
{
this.HasRequired(t => t.Applicant)
.WithMany(t => t.Certifications)
.HasForeignKey(d => d.ApplicantID);
}
}
public class ApplicantConfig : EntityTypeConfiguration<Applicant>
{
public ApplicantConfig()
{
}
}
Notice that the configuration classes inherit from EntityTypeConfiguration<T>
which comes from (using
System.Data.Entity.ModelConfiguration;
), this belongs to entity framework and it's responsible for the configuration of entities (tables) in DB.
Create Repositories (Pattern) (Inside Data Class Library Project)
Why Repository?
- One place to retrieve update the data, hence maintainability.
- Let's say you want to update applicant in your DB by talking to the
DBContext
directly, you can do that but you will have to write the same code each time you want to update but in different places, Repository will make sure that you never repeat your code (update in our example) again, hence reusability. - Following the single responsibility principle (SRP), by making sure that one place is responsible for retrieving or updating your DB.
Let's see how our repository will look like:
public interface IRepository<T> where T : class
{
IQueryable<T> GetAll();
T GetById(int id);
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(int id);
}
public class MyRepository<T> : IRepository<T> where T : class
{
public MyRepository(DbContext dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("Null DbContext");
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
protected DbContext DbContext { get; set; }
protected DbSet<T> DbSet { get; set; }
public virtual IQueryable<T> GetAll()
{
return DbSet;
}
public virtual T GetById(int id)
{
return DbSet.Find(id);
}
public virtual void Add(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Detached)
{
dbEntityEntry.State = EntityState.Added;
}
else
{
DbSet.Add(entity);
}
}
public virtual void Update(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
DbSet.Attach(entity);
}
dbEntityEntry.State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Deleted)
{
dbEntityEntry.State = EntityState.Deleted;
}
else
{
DbSet.Attach(entity);
DbSet.Remove(entity);
}
}
public virtual void Delete(int id)
{
var entity = GetById(id);
if (entity == null) return;
Delete(entity);
}
}
Before we dive in, we should know why I choose generic Repository
class, why not concrete class like MyApplicantRepository
or MySkillRepository
? Well, you can and it will still be Repository Pattern, but when you think about it, you will have to build a repository class for each Entity you have. In our example, we will end up writing 3 repositories. In normal projects, you will have 30 may be more, that means you will have to write new 30 repositories and that is not easy to maintain.
But when we use our Generic Repository, you can use it for all our Entities, easy to maintain and reuse.
First thing, we want to associate our repository to our Dbcontext
. That will be done in the repository constructor:
public MyRepository(MyExperienceDBContext dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("Null DbContext");
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
Because DbContext
is not of specific type, we will set that in this line:
( DbSet =DbContext.Set<T>();
) inside the repository constructor, the DbSet
will be the same type as of our generic class <T>
.
In the interface IRepository
, we have the following methods which MyRepository
will have to implement:
IQueryable<T> GetAll();
T GetById(int id);
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(int id);
In MyRepository
class, we are implementing IRepository
methods. You can have a look at how we implement those methods, also you can modify them or add new of your own.
public virtual void Update(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
DbSet.Attach(entity);
}
dbEntityEntry.State = EntityState.Modified;
}
For example, in the update method above, we check if the entity state is attached (DbContext
knows about it) or not, if attached that means we can track the modifications, if not attached we have to attach it to our DbContext
so we can track the new modification. Finally, we will change its status to modified to alert SaveChanges
that we have changed entity.
You can go and check this link for more information about the states of the DbContext
entities.
Now, let's start customizing our Repository
class. I will assume that we want to get all applicants with specific skill, but we don't have this method in our Standard repository class!!! And also, this method is only meant for applicants
entity (not applicable for Skills
or Certifications
entities) so what are we going to do???
Simple, we will create customize repository for applicant and at the same time, we will reuse the main repository, how??
Here, I started by creating IRepoApplicant
which will inherit from IRepository<Applicant>
as shown below:
public interface IRepoApplicant:IRepository<Applicant>
{
IQueryable<Applicant> GetApplicantsWihSkillName(string SkillName);
}
Here, you can see that I have created method GetApplicantsWihSkillName (string SkillName);
, let's take a look at the implementation:
public class RepoApplicant :MyRepository<Applicant> ,IRepoApplicant
{
public RepoApplicant(DbContext context):base(context)
{
}
public IQueryable<Applicant> GetApplicantsWihSkillName(string SkillName)
{
return GetAll().Where(p => p.Skills.Any
(o => o.Description.ToLower() == SkillName.ToLower() ));
}
}
First, as you can see, we inherit from MyRepository<Applicant>
, we know that this is for applicant
Entity, hence the Applicant
generic we pass to MyRepository
.
Second, notice that the constructor will pass its DbContext
to MyRepository
.
Now, we can reuse everything from main repository and our new customized Repository for applicants.
Create UOW (Unit Of Work pattern) (Inside Data Class Library Project)
UOW is the final peace of our application.
Why We Use UOW?
UOW acts like a facade that will aggregate all our repositories initiation, calls and disposal in one place, and also separates (decouples) our application (Consol, Controllers, ASP.NET Web Page) from DbContext
and repositories.
UOW will also take care of saving all our changes from multiple repositories in one place that we will create called commit
method.
Let's see how the UOW will look like:
public interface IMyExperienceUow
{
void Commit();
IRepository<Skill> Skills { get; }
IRepository<Certification> Certifications { get; }
IRepoApplicant Applicants { get; }
}
As you can see, its simple interface will expose our repositories and the commit
method.
public class MyExperienceUow : IMyExperienceUow, IDisposable
{
private MyExperienceDBContext DbContext { get; set; }
public MyExperienceUow()
{
CreateDbContext();
}
#region Repositries
private IRepository<Skill> _skills;
private IRepository<Certification> _certifications;
private IRepoApplicant _applicants;
public IRepository<Skill> Skills
{
get
{
if (_skills == null)
{
_skills = new MyRepository<Skill>(DbContext);
}
return _skills;
}
}
public IRepository<Certification> Certifications
{
get
{
if (_certifications == null)
{
_certifications = new MyRepository<Certification>(DbContext);
}
return _certifications;
}
}
public IRepoApplicant Applicants
{
get
{
if (_applicants == null)
{
_applicants = new RepoApplicant(DbContext);
}
return _applicants;
}
}
#endregion
public void Commit()
{
DbContext.SaveChanges();
}
protected void CreateDbContext()
{
DbContext = new MyExperienceDBContext();
DbContext.Configuration.ProxyCreationEnabled = false;
DbContext.Configuration.LazyLoadingEnabled = false;
DbContext.Configuration.ValidateOnSaveEnabled = false;
}
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (DbContext != null)
{
DbContext.Dispose();
}
}
}
#endregion
}
Notice in the constructor, we call method CreateDbContext()
which will take care of two things:
- Initiating the
DbContext
( DbContext = new MyExperienceDBContext();
) - Configuring Our
DbContext
, for example you can set the lazyloading
to false
.
Please note that those configurations will be applied to all repositories.
Finally, we get repository as property, this property will take care of initiating the specific repository if not initiated.
public IRepoApplicant Applicants
{
get
{
if (_applicants == null)
{
_applicants = new RepoApplicant(DbContext);
}
return _applicants;
}
}
Now we are almost ready to lunch this monster application :), but first, we have to check our console application (maybe your application is MVC or ASP.NET but I'm using console) for a couple of small things:
- We will make sure that we have Connection string named ("
MyExperience
") in our config file (App.config) inside of our console project, and it points to a new DB (No Tables).
You can change the name of the connection string to whatever you like, but make sure to change it also inside the MyExperienceDBContext
constructor, as shown below:
public partial class MyExperienceDBContext : DbContext
{
public MyInfoDBContext()
: base("Name=MyExperience")
{
}
- Add Entity Framework to our console Project from NuGet, the same way we did add entity to Data Project. check (Figure 5).
- Add references in our console Project to Model and Data Projects , as shown in (Figure 7).
Figure 7
- Add the code to your console application inside the main.
var newCertification = new Certification();
newCertification.Active = 1;
newCertification.Description = "MCP";
newCertification.Expire_Date = null;
newCertification.id = 1;
newCertification.Name = "Microsoft Profissional";
newCertification.Title = "Microsoft Web Development ASP.NET";
var newSkill1 = new Skill();
newSkill1.Active = 1;
newSkill1.Description = "C#";
newSkill1.id = 1;
var newSkill2 = new Skill();
newSkill2.Active = 1;
newSkill2.Description = "MVC";
newSkill2.id = 1;
var newApplicant = new Applicant();
newApplicant.Active = 1;
newApplicant.ApplicantID = 1;
newApplicant.Certifications.Add(newCertification);
newApplicant.Email = "master_khalil@yahoo.com";
newApplicant.First_Name = "Khaleel";
newApplicant.Last_Name = "Esbaitah";
newApplicant.LinkedIn_URL = "http://www.linkedin.com/pub/khaleel-esbaitah/1b/703/913";
newApplicant.Mobile_No = "34234234";
newApplicant.Objective = "To Join an organisation where I can emphasise my strength ";
newApplicant.Skills.Add(newSkill1);
newApplicant.Skills.Add(newSkill2);
newApplicant.Tel_No = "1234567";
newApplicant.Visa_Type = "PR";
IMyExperienceUow uow = new MyExperienceUow();
uow.Applicants.Add(newApplicant);
uow.Commit();
var result= uow.Applicants.GetAll();
We created new Skill
, Certification
and Applicant
.
Notice that we did not initiate repository or DbContext
, we just initiated New instance of our MyExperienceUow
and then added our newly created applicant
to Applicants
repository that already exists inside our UOW
class, then we committed the changes using Commit
method.
Please note that we will not have any table inside our DB, but when we start our application and we add new Applicant
then Commit
, the Entity framework is smart enough to notice there are no tables, so it will create the tables from our DbContext
and read our configurations.
Now, it's ready.
Important Note
In real life projects, 90% of the time the DB already exists, so what are we going to do??
Simple, there is a tool called "Entity Framework Power Tools". This basically will reverse engineer your DB to POCO Classes and DbContextClass
. For more information, go to this link.