ExampleProjetMyfinancePart2 BB
ExampleProjetMyfinancePart2 BB
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
DataBase
Reference
MyFinance.Service
MyFinance.Web MyFinance.Data
MyFinance.Domain
n
To add a reference follow these steps:
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.
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.
using MyFinance.Domain.Entities;
using System.Data.Entity;
2. Let’s add another class MyFinanceContextInitialize in the same file of the Context.
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.
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();
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; }
}
[DataType(DataType.MultilineText)]
public string Description { 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; }
//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
[NotMapped]
[Required(ErrorMessage = "Confirm Password is required")]
[DataType(DataType.Password)]
[Compare("Password")]
public string ConfirmPassword { get; set; }
[Required,EmailAddress]
public string Email { get; set; }
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();
//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
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:
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.
Step 3 : Update the Context to apply these custom convention so, in the “MyFinanceContext” class
override the “OnModelCreating” method like this :
How can we free up memory if any unmanaged resources remain? Solution: Dispose pattern
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);
}
- Next, we add a DatabaseFactory class which inherit from the class: Disposable and implement the
interface IDatabaseFactory.
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.
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!
T GetById(long id);
T GetById(string id);
IEnumerable<T> GetMany(Expression<Func<T, bool>> where = null,
Expression<Func<T, bool>> orderBy = null);
}
-Let’s add first an interface IRepositoryBaseAsync.
-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.
}
protected MyFinanceContext DataContext
{
get { return dataContext = databaseFactory.DataContext; }
}
Thus, we do not need to replicate CRUD methods; we can just add some specific methods in those
repositories.
}
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.
-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.
IDatabaseFactory dbFactory;
public UnitOfWork(IDatabaseFactory dbFactory)
{
this.dbFactory = dbFactory;
dataContext = dbFactory.DataContext;
}
Task<List<T>> GetAllAsync();
void Commit();
Task CommitAsync();
#region Constructor
protected Service(IUnitOfWork utwk)
{
this.utwk = utwk;
}
#endregion Constructor
return utwk.getRepository<TEntity>().GetById(id);
}
throw;
}
}
throw;
}
}
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 ProductService()
: base(ut)
{
}
}