0% found this document useful (0 votes)
28 views24 pages

ExampleProjetMyfinancePart2 BB

Uploaded by

mazlout hanadi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
28 views24 pages

ExampleProjetMyfinancePart2 BB

Uploaded by

mazlout hanadi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 24

Tutorial : 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 : We will implement the Disposable pattern, but first we should understand why we will use
this pattern.

 In fact there are two types of resources in the Framework .Net:


o Managed resources are resources that the garbage collector can be dispose when
there the reference is broken in the memory.
o Unmanaged resources that are all that the garbage collector can not handle,
example: open files, database connections ...

How can we free up memory if any unmanaged resources remain?  Solution: Dispose pattern

This is a design pattern that is used to clean unmanaged resources.

 To provide explicit control, implement the Dispose method provided by the IDisposable
interface. The consumer of the object should call this method when it has finished using the
object.
 Note that you must provide implicit cleanup using the Finalize () method, even when you
provide explicit control with Dispose. Finalize () provides a backup to prevent a definitive
resource drain if the programmer does not call Dispose.

Add a class Disposable which inherit from the IDisposable interface in the “Infrastructure” folder.
public class Disposable : IDisposable
{
// Track whether Dispose has been called.
private bool isDisposed;
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.

~Disposable()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.

Dispose(false);
}

public void Dispose()


{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
private void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!isDisposed && disposing)
{
DisposeCore();
}
isDisposed = true;
}

protected virtual void DisposeCore()


{
}
}
Step 3 : Then we will add an interface IDatabaseFactory which inherit from the interface
IDisposable in the “Infrastructure” folder.

public interface IDatabaseFactory : IDisposable


{
MyFinanceContext DataContext { get; }
}

- Next, we add a DatabaseFactory class which inherit from the class: Disposable and implement the
interface IDatabaseFactory.

public class DatabaseFactory : Disposable, IDatabaseFactory


{
private MyFinanceContext dataContext;
public MyFinanceContext DataContext { get { return dataContext; } }

public DatabaseFactory()
{
dataContext = new MyFinanceContext();
}
protected override void DisposeCore()
{
if (DataContext != null)
DataContext.Dispose();
}

-This class implement the design pattern Factory, in fact before talking about this design pattern,
let’s see two great principals that stands on the base of many design and architecture decisions.

 Separation of Concerns (SoC) – is the process of breaking a computer program into distinct
features that overlap in functionality as little as possible. A concern is any piece of interest or
focus in a program. Typically, concerns are synonymous with features or behaviors.
 Single Responsibility Principle (SRP) – every object should have a single responsibility, and
that all its services should be narrowly aligned with that responsibility. On some level
Cohesion is considered as synonym for SRP.

The separation allows:

● To allow people to work on individual pieces of the system in isolation;


● To facilitate reusability;
● To ensure the maintainability of a system;
● To add new features easily;
● To enable everyone to better understand the system;
-Therefore we choose the Factory pattern which will be responsible for the instantiation of our
Context and will act as a factory that create the Context object and yield it to the other classes. So,
as we can see this class is divided into two parts.

1. The first part instantiate the MyFinanceContext in the first call


2. The second part dispose the current object.

Step 4 :-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;
IDatabaseFactory databaseFactory;
public RepositoryBase(IDatabaseFactory dbFactory)
{
this.databaseFactory = dbFactory;
dbset = DataContext.Set<T>();

}
protected MyFinanceContext DataContext
{
get { return dataContext = databaseFactory.DataContext; }
}

#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> 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 10 :-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;

IDatabaseFactory dbFactory;
public UnitOfWork(IDatabaseFactory dbFactory)
{
this.dbFactory = dbFactory;
dataContext = dbFactory.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>(dbFactory);
return repo;
}

Part 8 – Business Layer


Step 1:-We add a common Service layer (Service.Pattern), Let’s add an interface to encapsulate the
methods implemented in the data layer:

public interface IService<T>:IDisposable


where T : class
{
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(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);
T Get(Expression<Func<T, bool>> where);
Task<int> CountAsync();

Task<List<T>> GetAllAsync();

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

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

void Commit();
Task CommitAsync();

-Now add the class Service that implements this interface:

public abstract class Service<TEntity> : IService<TEntity> where TEntity : class


{
#region Private Fields
IUnitOfWork utwk;
#endregion Private Fields

#region Constructor
protected Service(IUnitOfWork utwk)
{
this.utwk = utwk;
}
#endregion Constructor

public virtual void Add(TEntity entity)


{
utwk.getRepository<TEntity>().Add(entity);
}

public virtual void Update(TEntity entity)


{
utwk.getRepository<TEntity>().Update(entity);
}

public virtual void Delete(TEntity entity)


{
utwk.getRepository<TEntity>().Delete(entity);
}

public virtual void Delete(Expression<Func<TEntity, bool>> where)


{
utwk.getRepository<TEntity>().Delete(where);
}
public virtual TEntity GetById(long id)
{

return utwk.getRepository<TEntity>().GetById(id);
}

public virtual TEntity GetById(string id)


{
return utwk.getRepository<TEntity>().GetById(id);
}

public virtual IEnumerable<TEntity> GetMany(Expression<Func<TEntity, bool>>


filter = null, Expression<Func<TEntity, bool>> orderBy = null)
{
return utwk.getRepository<TEntity>().GetMany(filter,orderBy);
}

public virtual TEntity Get(Expression<Func<TEntity, bool>> where)


{
return utwk.getRepository<TEntity>().Get(where);
}

public virtual async Task<int> CountAsync()


{
return await utwk.getRepository<TEntity>().CountAsync();
}

public virtual async Task<List<TEntity>> GetAllAsync()


{
return await utwk.getRepository<TEntity>().GetAllAsync();
}

public virtual async Task<TEntity> FindAsync(Expression<Func<TEntity, bool>>


match)
{
return await utwk.getRepository<TEntity>().FindAsync(match);
}

public virtual async Task<List<TEntity>> FindAllAsync(Expression<Func<TEntity,


bool>> match)
{
return await utwk.getRepository<TEntity>().FindAllAsync(match);
}

public void Commit()


{
try
{
utwk.Commit();
}
catch (Exception ex)
{

throw;
}
}

public async Task CommitAsync()


{
try
{
await utwk.CommitAsync();
}
catch (Exception ex)
{

throw;
}
}

public void Dispose()


{
utwk.Dispose();
}
}

Step 2:-We move to the business layer (MyFinance.Service), Let’s add a service that inherit from the
generic service done in the previous step:

public interface IProductService : IService<Product>


{

public class ProductService : Service<Product>, IProductService


{

private static IDatabaseFactory dbf = new DatabaseFactory();


private static IUnitOfWork ut = new UnitOfWork(dbf);

public ProductService()
: base(ut)
{

}
}

You might also like