You are on page 1of 20

Correction EF Code First VF

Part 0 – Introduction:
Our main goal is to develop a MVC website for a hypermarket using .NET framework and
following this architecture:
User

Web Mvc

(MyFiance.Web)

Front-Office
Back-Office

(MyFinance.Domain)
BLL : Business Logic Layer
(MyFinance.Service) Entities Layer

DAL : Data Access Layer


(MyFinance.Data)

DataBase

Part 1 - Setting up a solution:


Create these projects in one solution named “MyFinance”
• “MyFinance.Domain”, “ MyFinance.Service”, “MyFinance.Data” ( 3 Class
Library Project)
• MyFinance.Web (ASP.NET Web Application (mvc))
Add the references following this schemas.

Reference
MyFinance.Service

MyFinance.Web MyFinance.Data

MyFinance.Domain
n
To add a reference follow these steps:

1. Right click on project that needs the reference


2. Click Add
3. Click Reference

4. In the Reference Manager Message box, check referenced projects

5. Click OK
Part 2 – Entities and Context Implementation:
Step 1 :
In the “MyFinance.Domain”, you should create a new folder named “Entities” that includes all the
classes of the following classes’ diagram, don’t forget navigation properties.

The following code expose the different entities:


public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
//navigation properties
virtual public ICollection<Product> Products { get; set; }
}

public class Provider


{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public string Email { get; set; }
public bool IsApproved { get; set; }
public DateTime? DateCreated { get; set; } // ? ➔ nullable
//navigation properties
virtual public ICollection<Product> Products { get; set; }
}

public class Product


{
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public int Quantity { get; set; }
public DateTime DateProd { get; set; }
//foreign Key properties
public int? CategoryId { get; set; }
//navigation properties
public virtual Category Category { get; set; }
public virtual ICollection<Provider> Providers { get; set; }
}

public class Biological : Product


{
public string Herbs { get; set; }
}

public class Chemical : Product


{
public string LabName { get; set; }
public string City { get; set; }
public string StreetAddress { get; set; }
}

We've made the navigation properties virtual so that we get lazy loading.
DateTime? Type is the nullable DateTime
We used ICollection interface so we can use later any collection implementation.

Step 2 : Entity Framework Installation


Now, let’s move to “MyFinance.Data” project and add a reference to the Entity-Framework.
So let’s open Manage NuGet package.
We need this reference so we can implement the context.
Do the same for “MyFinance.Web” project. Because entity framework won’t be deployed
unless it’s referenced by the web project

We install the Entity Framework package


Step 3 : Context Implémentation
A Context is a class inherits DbContext, and manage entities and database.

Add a new Context by following these steps:


1. Create a class “MyFinanceContext” in “MyFinance.Data” project
2. Add an inheritance from DbContext
3. Add a default constructor that calls the parent one, and give it the named connection
string “DefaultConnection” that exists in the web.Config file in “MyFinance.web”
project
4. Add sets (Collection for entities management)
5. Add necessary using.
using MyFinance.Domain.Entities;
using System.Data.Entity;

public class MyFinanceContext : DbContext Web.config


{
public MyFinanceContext()
: base("Name=DefaultConnection")
{
}

public DbSet<Category> Categories { get; set; }


public DbSet<Provider> Providers { get; set; }
public DbSet<Product> Products { get; set; }
}

Step 4 : Scaffold a Controller


1. In “MyFinance.Web” Project add a reference to “MyFinance.Data” project (Context)
2. Build the solution
3. Right click on “Controllers” folder in the “MyFinance.Web” Project
4. click “Add”
5. click “controller”
6. in the message box choose : “MVC5 Controller with views, using entity framework”
7. Fill the message box like this :
8. Click Add
9. Explore the generated Controller and Views
Step 5 : Run and enjoy ☺
Follow these steps:

1. Right click the “MyFinance.Web” project


2. Click “Set as start-up project”
3. Click the button
4. Add “/products” to the url and visit the page Ex : “http://localhost:{your port}/products”
5. Insert some data
Step 6 explore database
Select App_Data folder in “MyFinance.Web” and
click on show all files icon

Open App_Data folder and double click on the mdf


File

By default, Entity framework code first uses Table


Per Hierarchy (TPH) method to handle inheritance when
creating tables from entities. in TPH, All Data in the
hierarchy will be saved in a single database table and it
uses a Discriminator column to identify which record
belongs to which sub type. The value of this column will
be the name of the subtype.
Part 3 – Update the model:
Step 1 (Optional):-Let’s try to add a property public string Image { get; set; } to the entity Product.
1. Run the application. Try to list products. What happen?

The application throws this exception:

-That exception is thrown because the


model changed and the the Entity
Framework Code First doesn’t know how it
will behave. Therefore we have to tell the
framework how it must behave.

2. Let’s add another class MyFinanceContextInitialize in the same file of the Context.

This class inherit from DropCreateDatabaseIfModelChanges<MyFinanceContext>, which give the


Entity Framework the strategy of initializing the application. In other word, this class informs the
Entity Framework to drop and create the database if the model changes. We can replace this
class by DropCreateDatabaseAlways or CreateDatabaseIfNotExists.

3. Override the Seed method inherited from the base class.

This method serves to seed the database in case on dropping and creating the database, so we can
seed some data for test instead of adding data manually in the database each time the database is
created.

public class MyFinanceContextInitializer :


DropCreateDatabaseIfModelChanges<MyFinanceContext>
{
protected override void Seed(MyFinanceContext context)
{
var listCategories = new List<Category>{
new Category{Name="Medicament" },
new Category{Name="Vetement" },
new Category{Name="Meuble" },
};

context.Categories.AddRange(listCategories);
context.SaveChanges();
}
}

4. In order to set the initializer we have to add this code to the constructor of the class
MyFinanceContext.
Database.SetInitializer<MyFinanceContext>(new MyFinanceContextInitializer());
5. In “MyFinance.Web” project, delete the controller “ProductsController” and the “Products”
folder under the “Views” Folder and scaffold “ProductsController”. (same steps as Part2,
Step4) to refresh the views with the new added property
6. Go to the server explorer panel, right click on your database and close connection.
This Step is necessary to close all connection to the database, you delete the database in the
SQL Server Object Explorer or Restart the visual studio
7. Run the application another time and check the database

Step 2 : -
Assuming that we are using a production environment and we have important data. Then we are not
allowed to lose this data. In this case the initialisation strategy doesn't seem to be a good choice. So
let’s try something else, Migration !!!! Let’s do it.

1. The first step: we have to enable the Migration: Open the Package Manager Console the
choose the project “MyFinance.Data” which contains the class Context. Then execute this
command Enable-Migrations

Entity Framework Code First generate a new folder “Migration” which contains two classes
xxx_InitialCreate and Configuration.
-The first class contains an Up () method that contains instructions to create the table with its original
definition, and another method Down () to delete the table.
-The second class define the behavior of the migration for DbContext used. This is where you can
specify the data to insert, enter the providers of other databases …

2. The second step: we will change the name of the property public string Image { get; set; } to
public string ImageName { get; set; }.
3. Execute the command Add-Migration ModifyNameImage in the Package Manager Console.
4. The Add-migration command checks for changes since your last migration and scaffold a
new migration with any changes found. We can give a name for our migration, in this case,
we call migration "ModifyNameImage".
5. Go to the “Configuration” class in “Migrations” folder and add the following code in the seed
method of the migration :

context.Categories.AddOrUpdate(
p => p.Name, //Uniqueness property
new Category { Name = "Medicament" },
new Category { Name = "Vetement" },
new Category { Name = "Meuble" }
);
context.SaveChanges();

6. Execute the command Update-Database -TargetMigration:"ModifyNameImage"


in the Package Manager Console. This command will apply any pending migration to the
database and then check the database.
Part 4 – Complex Type:
Step 1 : Let’s add a Complex Type “Address” in “entities” folder in “MyFinance.Domain” project

public class Address {


public string StreetAddress { get; set; }
public string City { get; set; }

To create a new Complex Type we have to follow those rules otherwise we have to configure a
complex type using either the data annotations or the fluent API.

Step 2 : Let’s update “Chemical” entity with the complex type as the following :
public class Chemical : Product
{
public string LabName { get; set; }
public Address LabAddress { get; set; }
}

Part 5 – Configuration using annotations:


Step 1 : Add a reference to “System.ComponentModel.DataAnnotions” assembly to the
“MyFinance.Model” project
Step 2 : Now , we will add different data annotations to configure those entities.
● In class Product : properties should be configured as the following :
○ the property Name should be
■ required
■ The user input string have the length 25 (max)
■ The property have length 50 (max)
■ An error message will be displayed if the rules are not respected.
○ The property Description should be
■ Multiline
○ The property Price should be
■ Currency
○ The property Quantity should be
■ Positive integer
○ The property DateProd should be
■ Displayed as “Production Date”
■ Valid Date
○ The property CategoryId should be
■ The foreign Key property to the Category entity.
public class Product
{
public int ProductId { get; set; }

[DataType(DataType.MultilineText)]
public string Description { get; set; }

[Required(ErrorMessage = "Name Required")]


[StringLength(25, ErrorMessage = "Must be less than 25 characters")]
[MaxLength(50)]
public string Name { get; set; }

[Range(0,double.MaxValue)]
[DataType(DataType.Currency)]
public double Price { get; set; }

[Range(0, int.MaxValue)]
public int Quantity { get; set; }

[DataType(DataType.ImageUrl),Display(Name = "Image")]
public string ImageName { get; set; }

[Display(Name = "Production Date")]


[DataType(DataType.DateTime)]
public DateTime DateProd { get; set; }

//foreign Key properties


public int? CategoryId { get; set; }

//navigation properties
[ForeignKey("CategoryId ")] //useless in this case
public virtual Category Category { get; set; }
public virtual ICollection<Provider> Providers { get; set; }

}
MaxLength is used for the Entity Framework to decide how large to make a string value field when it
creates the database.
StringLength is a data annotation that will be used for validation of user input.
DataType : is used to specify a specific type : email, currency, card number …
Display is used to change the displayed name of the property
Range is used to limit valid inputs between min and max values.
Foreignkey : To configure the foreign key

● In class Provider : properties should be configured as the following :


○ the property Id should be
■ Key (Id is already a primary key By Convention)
○ The property Password should be
■ Password (hidden characters in the input)
■ Minimum length 8 characters
■ Required
○ The property ConfirmPassword should be
■ Required
■ Not mapped in the database
■ Password
■ Same value as “Password” property
○ The property Email should be
■ Email
■ Required
public class Provider
{
[Key] // optional !
public int Id { get; set; }

public string UserName { get; set; }

[Required(ErrorMessage = "Password is required")]


[DataType(DataType.Password)]
[MinLength(8)]
public string Password { get; set; }

[NotMapped]
[Required(ErrorMessage = "Confirm Password is required")]
[DataType(DataType.Password)]
[Compare("Password")]
public string ConfirmPassword { get; set; }

[Required,EmailAddress]
public string Email { get; set; }

public bool IsApproved { get; set; }

public DateTime? DateCreated { get; set; }

virtual public List<Product> Products { get; set; }


}
Key Define the Key column in the database..
NotMapped : no column would be generated in the database
Comapre : compare two properties
MinLength the opposite of MaxLength

Step 3 : Let’s Test


1. Update the database using migration
2. Delete « ProductsController » and products folder under Views folder
3. scaffold a new « ProductsController »
4. scaffold « ProvidersController »
5. Run and test

Part 6 – Configuration using fluent API:


Note that the data annotations and the fluent API configuration could coexist.

Step 1 : Create new Folder named “Configurations” in “MyFinance.Data” project


Step 2 : Add CategoryConfiguration classes in the “Configurations” folder
public class CategoryConfiguration : EntityTypeConfiguration<Category>
{
public CategoryConfiguration()
{
ToTable("MyCategories");
HasKey(c => c.CategoryId);
Property(c => c.Name).HasMaxLength(50).IsRequired();
}
}

-This class inherit from the generic class EntityTypeConfiguration , defined in


« System.Data.Entity.ModelConfiguration », then we pass the type that we want to configure
-In fact, Following the convention Entity Framework names the table created in the database with
the name of the entity in plural. In this case the table generated will be named Categories. Therefore,
when we want to change the name of the table we use the method ToTable(“MyCategories”).
-We will apply some rules on the Name and the Description proprieties

Step 3 : Add ProductConfiguration classes in the “Configurations” folder : This step contains 3 main
parts. The first part refers to the many-to-many association between products and providers, the
second part configure the inheritance, the third part configure the one-to-many association between
the Product and the Category.

public class ProductConfiguration : EntityTypeConfiguration<Product>


{
public ProductConfiguration()
{
//properties configuration
Property(e => e.Description).HasMaxLength(200).IsOptional();

//Many to Many configuration


HasMany(p => p.Providers)
.WithMany(v => v.Products)
.Map(m =>
{
m.ToTable("Providings"); //Table d'association
m.MapLeftKey("Product");
m.MapRightKey("Provider");
});

//Inheritance
Map<Biological>(c =>
{
c.Requires("IsBiological").HasValue(1); //isBiological is the descreminator
});
Map<Chemical>(c =>
{
c.Requires("IsBiological").HasValue(0);
});

//One To Many
HasOptional(p => p.Category) // 0,1..* //if you need 1..* use HasRequired()
.WithMany(c => c.Products)
.HasForeignKey(p => p.CategoryId)
.WillCascadeOnDelete(false);
} }
The Many to many part configure the association between the Product class and The Provider class

• Those classes have a many to many association. Entity Framework


uses the convention to create an association table following this
pattern. The name of the table will be the name of the first entity
concatenated with the name of the second one. This table will
contain two columns, those names will follow the next pattern:
[The_name_of_the_navigation_propriety]
[The_name_of_the_primary_Key]. This example explains better.
In this case we want to alter the name of the association table as
well as the names of the columns.

The inheritance part customizes the TPH inheritance:

• Indeed Entity Framework create a flag column named


Discriminator, so we want to change the name of the column to
IsBiological. This column will contain the value 1 in the case of
adding a Biological object. Otherwise it will contain the value 0.

The final part customizes the one-to-many association between the Product class and the Category
class.

• The Product can belong to a Category, which explains the optional relationship. In the
HasOptional method, we pass the navigation propriety Category of the Product Class. The
method WithMany specifies that the Category contains many Products. As well as the other
method, we pass the navigation navigation property Products of the Category class.
• We also want to disable the Cascade On Delete to conserve products after deleting an
associated category. For this aim the foreign key “CategoryId” should be nullable
Step 4 :Finally we configure the complex type Address by creating this class:

public class AddressConfiguration : ComplexTypeConfiguration<Address>


{
public AddressConfiguration()
{
Property(p => p.City).IsRequired();
Property(p => p.StreetAddress).HasMaxLength(50)
.IsOptional();
}
}

Step 5 : Update the Context to apply these configurations so, in the “MyFinanceContext” class
override the “OnModelCreating” method like this :

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
//If you want to remove all Convetions and work only with configuration :
// modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
modelBuilder.Configurations.Add(new CategoryConfiguration());
modelBuilder.Configurations.Add(new ProductConfiguration());
modelBuilder.Configurations.Add(new AddressConfiguration());
}

Step 6 : Update the database by migration and explore it to see what the configurations have done.

Part 7 – Create of custom convention:


Step 1 : Add a new folder in “MyFinance.Data” project called “CustomConventions”
Step 2 : We will create a class called DateTime2Convention in order to change to type of the
columns generated by DateTime proprieties.

public class DateTime2Convention : Convention


{
public DateTime2Convention()
{
this.Properties<DateTime>().Configure(c =>
c.HasColumnType("datetime2"));
}
}

Step 3 : Update the Context to apply these custom convention so, in the “MyFinanceContext” class
override the “OnModelCreating” method like this :

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
//If you want to remove all Convetions and work only with
configuration :
//
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
modelBuilder.Configurations.Add(new CategoryConfiguration());
modelBuilder.Configurations.Add(new ProductConfiguration());
modelBuilder.Configurations.Add(new AddressConfiguration());
modelBuilder.Conventions.Add(new DateTime2Convention());
}

Part 8 – Design Patterns:


Step 1 : Add a new folder in “MyFinance.Data” project called “Infrastructure”
Step 2 :-Let’s move now to the implementation of CRUD methods. No , we won’t use the DAO
classes. Why?

-In fact, the pattern DAO is an anti-pattern rather than a design pattern. Supposing that we have 100
pocos, then we have to create 100 DAO classes where we will replicate the same code 100 times.

-Moreover , when we want to modify the behavior of an update method for example , then we have
to do the same thing 100 times.

-Therefore , we will use the Repository pattern. The repository pattern is an abstraction layer which
provides a well-organised approach to maintaining a separation between an application's data access
and business logic layers. This gives us the important advantages of making code more maintainable
and readable and improving the testability of our code. It also works great with dependency
injection!

-Let’s add first an interface IRepositoryBase.

public interface IRepositoryBase<T>


where T : class
{
void Add(T entity);
void Delete(Expression<Func<T, bool>> where);
void Delete(T entity);
T Get(Expression<Func<T, bool>> where);

T GetById(long id);
T GetById(string id);
IEnumerable<T> GetMany(Expression<Func<T, bool>> where = null,
Expression<Func<T, bool>> orderBy = null);

void Update(T entity);

-Let’s add first an interface IRepositoryBaseAsync.

public interface IRepositoryBaseAsync<T> : IRepositoryBase<T>


where T : class
{
Task<int> CountAsync();
Task<List<T>> FindAllAsync(Expression<Func<T, bool>> match);
Task<T> FindAsync(Expression<Func<T, bool>> match);
Task<List<T>> GetAllAsync();
}

-Then we will add a RepositoryBase class. In the first hand, we create this abstract class is a generic
class which contain generics CRUD methods. In the second hand, we will add the different
repositories in which we will extend IRepositoryBaseAsync<T> of a specific entity.

-As we see in this class, we pass a DatabaseFactory object through the constructor and we create a
generic Set since this Set will be used by the different entities.

-After getting the Context object created by the DatabaseFactory we add some generics methods.

-Note that in somes methods like public virtual void Delete(Expression<Func<T, bool>> where)

we can pass a lambda expression to add a criteria instead of creating several methods to delete by
criteria.

public class RepositoryBase<T> : IRepositoryBaseAsync<T> where T : class


{
private MyFinanceContext dataContext;
private readonly IDbSet<T> dbset;

public RepositoryBase(MyFinanceContext dataContext)


{
this.dataContext = dataContext;
dbset = dataContext.Set<T>();

#region Synch Methods


public virtual void Add(T entity)
{
dbset.Add(entity);
}
public virtual void Update(T entity)
{
dbset.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
dbset.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = dbset.Where<T>(where).AsEnumerable();
foreach (T obj in objects)
dbset.Remove(obj);
}
public virtual T GetById(long id)
{
return dbset.Find(id);
}
public virtual T GetById(string id)
{
return dbset.Find(id);
}
//public virtual IEnumerable<T> GetAll()
//{
// return dbset.ToList();
//}

public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where = null,


Expression<Func<T, bool>> orderBy = null)
{
IQueryable<T> Query = dbset;
if (where != null)
{
Query = Query.Where(where);
}
if (orderBy != null)
{
Query = Query.OrderBy(orderBy);
}
return Query;
}
public T Get(Expression<Func<T, bool>> where)
{
return dbset.Where(where).FirstOrDefault<T>();
}
#endregion

#region Async methos

public async Task<int> CountAsync()


{
return await dbset.CountAsync();
}

public async Task<List<T>> GetAllAsync()


{
return await dbset.ToListAsync();
}

public async Task<T> FindAsync(Expression<Func<T, bool>> match)


{
return await dbset.SingleOrDefaultAsync(match);
}

public async Task<List<T>> FindAllAsync(Expression<Func<T, bool>> match)


{
return await dbset.Where(match).ToListAsync();
}
#endregion
}

Now, we create a “Repositories” Folder to add the different repositories. Then we add the following
repositories CategoryRepository.

Thus, we do not need to replicate CRUD methods; we can just add some specific methods in those
repositories.

public static class CategoryRepository


{
public static IEnumerable<Product> ProductByCategory(this
IRepositoryBaseAsync<Category> repository, int id)
{
return repository.GetById(id).Products.AsEnumerable();

}
public static IEnumerable<Product> MostExpensiveProductByCategory(this
IRepositoryBaseAsync<Category> repository,int id)
{
return repository.ProductByCategory(id)
.OrderByDescending(b => b.Price)
.Take(5)
.AsEnumerable();
}
public static IEnumerable<Product> GetProductsByCategory(this
IRepositoryBaseAsync<Product> repository,string categoryName)
{
return repository.GetMany(x => x.Category.Name == categoryName);
}

Step 3 :-Now we will see the last design pattern in our project The Unit Of Work.

-The idea is that we can use the Unit of Work to group a set of related operations

– the Unit of Work keeps track of the changes that we are interested in until we are ready to save
them to the database.

-The pattern Unit Of Work consists in making one or more transactions database committed or rolled
back together.

-The pattern "Unit of work" is used to:

-Group several "repositories" so they share a context of unique database (DbContext).

-Apply the SaveChanges () method on the instance of the context defined and ensure that any
changes connected to each other will be made in a coordinated manner.

-Let’s add the next interface IUnitOfWork in the Infrastructure folder:

public interface IUnitOfWork : IDisposable


{
IRepositoryBaseAsync<T> getRepository<T>() where T : class;
void CommitAsync();
void Commit();
void Dispose();
}

-Then add The UnitOfWork class:

public class UnitOfWork : IUnitOfWork


{

private MyFinanceContext dataContext;

public UnitOfWork( MyFinanceContext dataContext)


{
this.dataContext = dataContext;
}

public void Commit()


{
dataContext.SaveChanges();
}
public async Task CommitAsync()
{
await dataContext.SaveChangesAsync();
}
public void Dispose()
{
dataContext.Dispose();
}
public IRepositoryBaseAsync<T> getRepository<T>() where T : class
{
IRepositoryBaseAsync<T> repo = new RepositoryBase<T>(dataContext);
return repo;
}

Part 8 – Business Layer

Step 1:-We move to the business layer (MyFinance.Service), Let’s add an interface and a service
IEBuyService and EBuyService:

public interface IEBuyService


{

void CreateProduct(Product p);

void Commit();
void Dispose();

}
public class EBuyService : IEBuyService
{
MyFinanceContext dataContext = new MyFinanceContext();
IUnitOfWork utOfWork = null;
public EBuyService()
{

utOfWork = new UnitOfWork(dataContext);


}

public void CreateProduct(Product p)


{
utOfWork.getRepository<Product>().Add(p);

public void Commit()


{
utOfWork.Commit();
}
public void Dispose()
{
utOfWork.Dispose();
}
}

You might also like