Saturday, 21 September 2024

EF Core Migrations

What is EF Core Migrations

In EF core we create or update data models in our code. We call this approach the Code First approach. As our app grows, we are going to make a lot of changes to our model. This will result in our database going out of sync with the model.

Hence we need to update the database as we make changes to the model. But making changes manually is error-prone. We can also create a fresh database from the model, but it will result in loss of data.

The EF Core migrations make it easier to push the changes to the database and also preserve the existing data in the database. It provides commands like add-migration, remove-migration to create & remove migrations. Once migrations are generated, we update the database using the update-database. We can also generate the migration script and execute it in the production server.

How to Create Ef Core Migrations

Let us create a new console application to see how migrations work in entity framework core. Name the app as EFCoreMigration.

First, We will create a model and context class. Then we use the Migrations commands to create migrations and then later update the database.

Install the Microsof.Entity FrameworkCore.SqlServer package, which in turn installs the Entity Framework Core package

Install-package Microsoft.EntityFrameworkCore.SqlServer

Models for Migration

Create the Models folder and create two class Product.cs and Vendor.cs as shown below

Context: Under the models folder create the context class EFContext, which inherits from DBContext as shown below

using Microsoft.EntityFrameworkCore;
 
namespace EFCoreMigration.Models
{
    public class EFContext : DbContext
    {
 
        private const string connectionString = "Server=(localdb)\\mssqllocaldb;Database=EFCoreMigration;Trusted_Connection=True;";
 
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(connectionString);
        }
 
        public DbSet<Product> Products { get; set; }
        public DbSet<Vendor> Vendors { get; set; }
    }
}

Preparing for Migration

Open the Package Manager and run the following command.

Install-Package Microsoft.EntityFrameworkCore.Tools

Installing the above package also installs the Microsoft.EntityFrameworkCore.Design package. This package actually contains the commands like add migrations, Update database, scaffold an existing database by reverse-engineering the schema of a database, etc

List of Migration Commands

add migration

Whenever you create or Modify the domain classes, then the first thing you need to do is to create a Migration. This is done by add-migration command (or by using dotnet EF migrations add command line)

Open the Package Console manager and run the command

add-migration "initial" 

Inspecting the Migration scripts

The add-migration command created three classes under the folder Migrations. One is the Migration class and the second one is the designer class & the last one is snapshot class.

The add-migration command generates this file when it is run for the first time. In the subsequent runs, it uses it to find out the changes to be made to the model. It then overwrites it so that it remains up to date.

Update database

The add-migration creates the migration package but does not create the database.

The update-database command is used to bring the database in sync with the latest migration or the migration name specified as the argument

Run the update-database as shown below

update-database  

Open the SQL Server and you will see that the tables Products & vendors are created in the Database. Also, note that the _EFMigrationsHistory table.

__EFMigrationsHistory

This table keeps track of the Migrations applied to the database. It contains two columns one is MigrationId and ProductVersion. MigrationId contains the Unique ID of the migration and ProductVersion is the version number of Entity Framework used.

The update-databasechecks if the database exists. If the table __EFMigrationsHistory does not exists then the update-database applies the migration from the beginning and then creates the __EFMigrationsHistory

If __EFMigrationsHistory already exists, then update-database checks the to see the last migration applied and then continues to apply the migrations from the last migrations to the latest. It then inserts latest migration id in the __EFMigrationsHistory table

Update-database never checks the database schema to verify whether it matches with the model. So if you accidentally delete the __EFMigrationsHistory table, alter or delete any other table or fields, the EF Core never reports an error. The error occurs only when the App is running and you query the altered/deleted table

Connection string for update-database

The update-database requires us to create the OnConfiguring() method in the context class and expects us to pass the correct connection string to the desired database provider. For example if you are using the Sql Server then you need to call the method UseSqlServer(ConnectionString)

That is because the update-database instantiates the Context class and executes the onConfiguring method so as to configure the context. It is unaware of how and where you have stored the connection string. And it does not have the access to Dependency injection container as it runs outside the project.

Reference: https://www.tektutorialshub.com/


Continue Reading →

@Self, @SkipSelf & @Optional Decorators Angular

@Self, @SkipSelf and @Optional are Angular Decorators that configure how the DI Framework should resolve the dependencies. These decorators are called Resolution Modifiers because they modify the behavior of injectors. In this page, we will learn @Self, @SkipSelf, & @Optional. 

 How Angular DI Framework Resolves Dependencies

When a component asks for Dependency, the DI Framework resolves it in two phases.

In the first phase, it starts to look for the Dependency in the current component’s ElementInjector. If it does not provide the Dependency, it will look in the Parent Components ElementInjector. The Request bubbles up until it finds an injector that provides the service or reaches the root ElementInjector.

If ElementInjector does not satisfy the request, Angular looks for the Dependency in the ModuleInjector hierarchy. If Angular still doesn’t find the provider, it throws an error.

We have created an example project in Angular to explain the @Self, @SkipSelf, & @Optional. 

The Code contains a RandomService, which generates a Random Number when initialized. The Angular Service is added to the Providers array of the AppModule. We can inject this service anywhere in our Application.

@Injectable({
  providedIn: "root"
})
export class RandomService {

The project contains three Angular Components (AppComponent, ChildComponent & GrandChildComponent) all inject the RandomService and displays the Random Number from the Service.

We also have testDirective, which we include in the template of GrandChildComponent. It also displays the Random Number from the Service.

Ensure that the Providers array is empty in all components & directives. Run the App. Angular creates only one instance of the RandomService. That is why all the components and directives show the same number.


@Self: The @Self decorator instructs Angular to look for the dependency only in the local injector. The local injector is the injector that is part of the current component or directive.

import { Component, SkipSelf, Self, Optional, Host } from "@angular/core";
import { RandomService } from "./random-service";

@Component({
  selector: "my-grandChild",
  template: `
    <div class="box">
      GrandChildComponent => {{ randomNo }}
      <div class="dirbox" testDirective>fdf</div>
    </div>
  `,
  providers: [RandomService]
})
export class GrandChildComponent {
  randomNo;
  constructor(@Self() private randomService: RandomService) {
    this.randomNo = randomService.RandomNo;
  }

This forces the Angular DI Framework to look for the Dependency attached to the current Component. I added the RandomService to the providers array of the GrandChildComponent.

As you can see from the image Angular creates two instances of RandomService. One from the AppModule and another from the GrandChildComponent. Also, note that testDirective picks up the RandomService provided from the GrandChildComponent and not from the AppModule

@SkipSelf: The @SkipSelf decorator instructs Angular to look for the dependency in the Parent Injector and upwards.

It tells Angular not to look for the injector in the local injector, but start from the Parent. You can think of this decorator as the opposite of the @Self

Open the GrandChildComponent again. Add the SkipSelf instead of Self decorator.

export class GrandChildComponent {
  randomNo;
  constructor(@SkipSelf() private randomService: RandomService) {
    this.randomNo = randomService.RandomNo;
  }
}

As you can see from the image, the GrandChildComponent, picks up RandomService instance provided by the Module and not the one provided by itself. 
But, the testDirective still picks up the RandomService provided by the GrandChildComponent.

@Optional: Optional marks the dependency as Optional. If the dependency is not found, then it returns null instead of throwing an error

In the GrandChildComponent remove the RandomService from the Providers Array and add the @Self decorator. You will instantly receive the error “No provider for RandomService found in NodeInjector“.

Add the @Optional decorator along with the @Self. Now, the dependency injection will return null instead of an error.

Also, remember to add the ? in randomService?, else you will get the “Cannot read property ‘RandomNo’ of null” error.

export class GrandChildComponent {
  randomNo;
  constructor(@Optional() @Self() private randomService: RandomService) {
    this.randomNo = randomService?.RandomNo;
  }
}

As you can see in the image, GrandChildComponent does not receive any values, while testDirective picks up the RandomService provided by the AppModule



Continue Reading →

Topics

ADFS (1) ADO .Net (1) Ajax (1) Angular (47) Angular Js (15) ASP .Net (14) Authentication (4) Azure (3) Breeze.js (1) C# (55) CD (1) CI (2) CloudComputing (2) Coding (10) CQRS (1) CSS (2) Design_Pattern (7) DevOps (4) DI (3) Dotnet (10) DotnetCore (20) Entity Framework (5) ExpressJS (4) Html (4) IIS (1) Javascript (17) Jquery (8) jwtToken (4) Lamda (3) Linq (10) microservice (4) Mongodb (1) MVC (46) NodeJS (8) React (10) SDLC (1) Sql Server (32) SSIS (3) SSO (1) TypeScript (3) UI (1) UnitTest (2) WCF (14) Web Api (16) Web Service (1) XMl (1)

Dotnet Guru Archives