Saturday, 10 August 2024

How to create and use middleware in asp.net core

Middleware is piece of code that's assembled into an app pipeline to handle requests and responses. 

  • Each middleware component in the request pipeline is responsible for invoking the next component in the pipeline or short-circuiting the pipeline. 
  • each component can perform work before and after the next component in the pipeline.

Request delegates are used to build the request pipeline.

Request delegates are configured using Run, Map, and Use extension methods. 

UseWhen also branches the request pipeline based on the result of the given predicate. Unlike with MapWhen, this (UseWhen) branch is rejoined to the main pipeline if it doesn't short-circuit or contain a terminal middleware.

app.UseWhen(context => context.Request.Query.ContainsKey("branch"),appBuilder => HandleBranchAndRejoin(appBuilder));

Custom Middleware: We can create custom middleware either by convention or impelementing IMiddle<T> interface.

Convention (Class) based Middleware: Following point need to remember while creating conventional middleware:

  • A public constructor with a parameter of type RequestDelegate.
  • A public method named Invoke or InvokeAsync. This method must return a Task and accept a first parameter of type HttpContext.
  • Additional parameters for the constructor and Invoke/InvokeAsync are populated by dependency injection (DI).

To use this middleware, we need to call USEMIDDLEWARE<CLSNAME>() extension method.

Note: Class based (Conventional) middleware has a singleton scope (application lifetime).

Per-request middleware dependencies

Middleware is constructed at app startup and therefore has application lifetime. Scoped lifetime services used by middleware constructors aren't shared with other dependency-injected types during each request. To share a scoped service between middleware and other types, add these services to the InvokeAsync method's signature. The InvokeAsync method can accept additional parameters that are populated by DI.

Factorybased Middleware

UseMiddleware extension methods check if a middleware's registered type implements IMiddleware. If it does, the IMiddlewareFactory instance registered in the container is used to resolve the IMiddleware implementation instead of using the convention-based middleware activation logic. The middleware is registered as a scoped or transient service in the app's service container.

IMiddleware is activated per client request (connection), so scoped services can be injected into the middleware's constructor.

IMiddleware defines middleware for the app's request pipeline. The InvokeAsync(HttpContext, RequestDelegate) method handles requests and returns a Task that represents the execution of the middleware.

USE: builder.Services.AddTransient<FactoryActivatedMiddleware>(); 

FactoryActivatedMiddleware is the name of class implements IMiddleware.


Wednesday, 24 July 2024

Docker commands

 Below are the some basic docker commands:

FROM: The FROM instruction specifies the base image that the container will be built on top of. This instruction is typically the first one in a Dockerfile and is used to set the base image for the container. The format of the instruction is:

FROM <image>

For example:

FROM node:14-alpine3.16

This instruction tells Docker to use the node:14-alpine3.16 image as the base image for the container. To use a specific version or tag of an image you can use:<version> or:<tag> syntax.

WORKDIR : In a Dockerfile, the WORKDIR instruction sets the working directory for any command that follows it in the Dockerfile. This means that any commands that are run in the container will be executed relative to the specified directory.

WORKDIR <directory>

For example:  WORKDIR /app

This instruction tells Docker to set the working directory of the container to /app . Any subsequent commands in the Dockerfile, such as COPY, RUN, or CMD, will be executed in this directory.

It’s important to note that the WORKDIR instruction creates the directory if it does not already exist. And the subsequent COPY and ADD commands will be executed relative to the WORKDIR specified.


COPY: Use the COPY instruction to copy local files from the host machine to the current working directory. For example, to copy a file named package.json from the host machine’s current directory to the image’s /app directory, you would use the following command:

COPY package.json /app/

If you want to copy all the files from the host’s current directory to the container’s current directory, you can use the below command:

COPY . .

It is used to copy all files and directories from the current directory on the host machine (indicated by “.”) to the current directory within the container. The first “.” refers to the source directory on the host machine, and the second “.” refers to the destination directory within the container.

RUN: Use the “RUN” instruction to execute commands that will run during the image build process. The format of instruction is :

RUN <command_name>

For example, to update the package manager and install a specific package, you would use the following command:

RUN npm install

CMD: In a Dockerfile, the CMD instruction sets the command that will be executed when a container is run from the image. The format of the instruction is:

CMD ["executable","param1","param2",...]

For example:

CMD ["npm", "start"]

This instruction tells Docker to run the command npm start when a container is created from the image. This command will start the Node.js application that was installed in the container using the npm install command.

ENV: Use the ENV instruction to set environment variables inside the image which will be available during build time as well as in a running container. For example, to set the NODE_ENV environment variable to production, you would use the following command:

ENV NODE_ENV production

EXPOSE: Use the EXPOSE command to tell Docker which ports the container will listen on at runtime. For example, if your application listens on port 9000, you will use the following command:

EXPOSE 9000

Sunday, 9 June 2024

Notes : filters in asp.net core


======================Middleware vs filters========

The main difference between them is their scope. Filters are a part of MVC, so they are scoped entirely to the MVC middleware. Middleware only has access to the HttpContext and anything added by preceding middleware. In contrast, filters have access to the wider MVC context, so can access routing data and model binding information 

for example.

Generally speaking, if you have a cross-cutting concern that is independent of MVC then using middleware makes sense, if your cross-cutting concern relies on MVC concepts, or must run midway through the MVC pipeline, then filters make sense.


==============================


ConsumesAttribute—Can be used to restrict the allowed formats an action method can accept. If your action is decorated with [Consumes("application/json")] but the client sends the request as XML, then the resource filter will short-circuit the pipeline and return a 415 Unsupported Media Type response.


==============================

 Exception filters can catch exceptions from more than your action methods.

They’ll run if an exception occurs in MvcMiddleware

 During model binding or validation

 When the action method is executing

 When an action filter is executing

You should note that exception filters won’t catch exceptions thrown in any filters

other than action filters, so it’s important your resource and result filters don’t throw

exceptions. Similarly, they won’t catch exceptions thrown when executing IActionResult itself. 


================================

ProducesAttribute—This forces the Web API result to be serialized to a specific output format. For example, decorating your action method with [Produces("application/xml")] forces the formatters to try to format the response as XML, even if the client doesn’t list XML in its Accept header.

FormatFilterAttribute—Decorating an action method with this filter tells the formatter to look for a route value or query string parameter called format, and to use that to determine the output format. For example, you could call /api/recipe/11?format=json and FormatFilter will format the response as JSON, or call api/recipe/11?format=xml and get the response as XML.


======================================


As with resource and action filters, result filters can implement a method that runs after the result has been executed, OnResultExecuted. You can use this method, for example, to inspect exceptions that happened during the execution of IActionResult.

========================================

Generally, you can’t modify the response in the OnResultExecuted method, as MvcMiddleware may have already started streaming the response to the client.

========================================

The most interesting point here is that short-circuiting an action filter doesn’t shortcircuit much of the pipeline at all. In fact, it only bypasses later action filters and the action method execution itself. By primarily building action filters, you can ensure that other filters, such as result filters that define the output format, run as usual, even when your action filters short-circuit. 

=======================================

The previous version of ASP.NET used filters, but they suffered from one problem in particular: it was hard to use services from them. This was a fundamental issue with implementing them as attributes that you decorate your actions with. C# attributes don’t let you pass dependencies into their constructors (other than constant values), and they’re created as singletons, so there’s only a single instance for the lifetime of your app.

========================================

the key is to split the filter into two. Instead of creating a class that’s both an attribute and a filter, create a filter class that contains the functionality and an attribute that tells MvcMiddleware when and where to use the filter.

a class implements IACtionFiler and Anoter class derived from TypeFilerAttribute, in this class we will pass type of IActionFiler implementing class so this will work as attribute.

===================================


Summary

 The filter pipeline executes as part of MvcMiddleware after routing has selected an action method.

 The filter pipeline consists of authorization filters, resource filters, action filters, exception filters, and Result filters. Each filter type is grouped into a stage.

 Resource, action, and result filters run twice in the pipeline: an *Executing method on the way in and an *Executed method on the way out.

 Authorization and exception filters only run once as part of the pipeline; they don’t run after a response has been generated.

 Each type of filter has both a sync and an async version. For example, resource filters can implement either the IResourceFilter interface or the IAsyncResourceFilter interface. You should use the synchronous interface unless your filter needs to use asynchronous method calls.

 You can add filters globally, at the controller level, or at the action level. This is called the scope of the filter. Within a given stage, global-scoped filters run first, then controller-scoped, and finally, action-scoped.

 You can override the default order by implementing the IOrderedFilter interface. Filters will run from lowest to highest Order and use scope to break ties.

 Authorization filters run first in the pipeline and control access to APIs. ASP.NET Core includes an [Authorization] attribute that you can apply to action methods so that only logged-in users can execute the action.

 Resource filters run after authorization filters, and again after a result has been executed. They can be used to short-circuit the pipeline, so that an action

method is never executed. They can also be used to customize the model binding process for an action method.

 Action filters run after model binding has occurred, just before an action method executes. They also run after the action method has executed. They can be used to extract common code out of an action method to prevent duplication. 

 The Controller base class also implements IActionFilter and IAsyncActionFilter. They run at the start and end of the action filter pipeline, regardless of the ordering or scope of other action filters.

 Exception filters execute after action filters, when an action method has thrown an exception. They can be used to provide custom error handling specific to the action executed. 

 Generally, you should handle exceptions at the middleware level, but exception filters let you customize how you handle exceptions for specific actions or controllers.

 Result filters run just before and after an IActionResult is executed. You can use them to control how the action result is executed, or to completely change the action result that will be executed.

 You can use ServiceFilterAttribute and TypeFilterAttribute to allow dependency injection in your custom filters. ServiceFilterAttribute requires that you register your filter and all its dependencies with the DI container, whereas TypeFilterAttribute only requires that the filter’s dependencies have been registered


Tuesday, 28 May 2024

Rate limiting in asp.net core

 There are 4 rate limiting algorithms:

Fixed window : The AddFixedWindowLimiter method uses a fixed time window to limit requests. When the time window expires, a new time window starts and the request limit is reset.

below are steps to configure fixed window rate limiter

1. Add Rate Limiter:

       builder.Services.AddRateLimiter(_ => _
                .AddFixedWindowLimiter(policyName: fixedPolicy, options =>
                {
                    options.PermitLimit = myOptions.PermitLimit;//2, getting values from config
                    options.Window = TimeSpan.FromSeconds(myOptions.Window);//2000 
                    options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
                    options.QueueLimit = myOptions.QueueLimit;// 2
                    
                }      
             ));
2.      app.UseRateLimiter();

3. call RequireRateLimiting("policyname like fixed") either on minimal api endpoint or MapControllerwithDefaultRoute().

4. We can use [EnableRateLimiter] , [DisableRateLimiter] attributes if we don't want to configure rate limiter globally.
5 we can configure multiple ratelimiting policies on AddRateLimiter and we can use specific policy for specific controller/Action method.

concurrency limiter :
The concurrency limiter limits the number of concurrent requests. Each request reduces the concurrency limit by one. When a request completes, the limit is increased by one. Unlike the other requests limiters that limit the total number of requests for a specified period, the concurrency limiter limits only the number of concurrent requests and doesn't cap the number of requests in a time period

Distributed caching using Redis in asp.net core.

 To use Redis for distributed caching in ASP.Net core you need to follow below steps:

  1. Need to add NuGet package "Microsoft.Extensions.Caching.StackExchangeRedis"
  2. Need to configure Redis service via 
            builder.Services.AddStackExchangeRedisCache(option =>{
                string connection = builder.Configuration.GetConnectionString("redis")!;
                option.Configuration = connection; //< == provide connection string;               
            });

     3.configure redis connection in AppSettings.json

"ConnectionStrings": {

    "redis": "localhost:6379"

  }

      4.You need to inject IDistributedCache dependency in Constructor or endpoint.

IDistributedCache provides GetString("StringKey") and SetString("StringKey","StringValue") API, to set your object in Cache you need to serialize it.

app.MapGet("/weatherforecast", (HttpContext httpContext, IDistributedCache cache) =>

            {

                var strCachedForcast = cache.GetString("Forecast");

                if (string.IsNullOrEmpty(strCachedForcast))

                {

                   var cachedForecaste = Enumerable.Range(1, 5).Select(index =>

                       new WeatherForecast

                       {

                           Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),

                           TemperatureC = Random.Shared.Next(-20, 55),

                           Summary = summaries[Random.Shared.Next(summaries.Length)]

                       }) .ToArray();

                     strCachedForcast =  JsonSerializer.Serialize< WeatherForecast[]>(cachedForecaste );

                    cache.SetString("Forecast", strCachedForcast);

                }

              return  JsonSerializer.Deserialize<WeatherForecast[]>(strCachedForcast);             

            })

            .WithName("GetWeatherForecast")

            .WithOpenApi();  

Monday, 27 May 2024

Calling transient service in Singleton service

 If you will call transient service within singleton service, transient service will be treated as singleton service, i.e. the instance of transient service will be available till the lifetime of singleton service/application.

 public interface ISingleton

    {

        void SingltonMethod();

    }

    public class Singleton : ISingleton

    {

        public Singleton(ITransient trnasient)

        {

            Console.WriteLine("Singleton ctor called");

            Transient = trnasient;

        }


        public ITransient Transient { get; }


        public void SingltonMethod()

        {

            Console.WriteLine("Singleton Method");

          Console.WriteLine("  Transient instance Id :" +Transient.GetHashCode());

        }

    }


    public interface ITransient

    {

        void TransientMethod();

    }

    public class Transient : ITransient

    {

        public Transient()

        {

            Console.WriteLine("Transient ctor called");

        }

        public void TransientMethod()

        {

            Console.WriteLine("Transient Method");

        }

    }


    public interface ITransientA

    {

        void TransientMethod();

    }

    public class TransientA : ITransientA

    {

        private readonly ITransient trns;


        public TransientA()//(ITransient trns)

        {

            Console.WriteLine("TransientA ctor called");

            //this.trns = trns;

        }

        public void TransientMethod()

        {

            Console.WriteLine("Transient A Method");

            //trns.TransientMethod();

        }

    }


Service Registration:

          builder.Services.AddSingleton<ISingleton, Singleton>();

            builder.Services.AddTransient<ITransient, Transient>();

            builder.Services.AddTransient<ITransientA, TransientA>();


Service injection:

         app.MapGet("/", (ISingleton service,ITransient transient , ITransientA transientA) => {

                service.SingltonMethod();

                transientA.TransientMethod();

                Console.WriteLine("transientA hashcode :"+ transientA.GetHashCode());

                 /transient.TransientMethod();

                 Console.WriteLine("transient hashcode :" + transient.GetHashCode());


             });


Output:

Custom middleware before

Transient ctor called

Singleton ctor called

Transient ctor called

TransientA ctor called

Singleton Method

  Transient instance Id :39449526

Transient A Method

transientA hashcode :50346327

Transient Method

transient hashcode :50874780

Custom middleware After!

Custom middleware before

Transient ctor called

TransientA ctor called

Singleton Method

  Transient instance Id :39449526

Transient A Method

transientA hashcode :11404313

Transient Method

transient hashcode :64923656

Custom middleware After!


clearly, we can see that instance of transient dependency within singleton is same all the time, while in other injections we are getting new instance.

Friday, 17 May 2024

Difference between HttpPut and HttpPost method

 Although technically, we can create and update resource with both Put and Post methods but as per standard we should use Post method to create a new resource while we should use Put method to update existing resource.

There are few differences between these two methods:

  1. While creating resource we don't have primary key of resource so in case of Post we don't have primary key in resource body but we have primary key of resource in case of Put.
  2. URI used for PUT method directly identify resource to update, while we will get a new URI for newly create resource by POST method.
  3. Most important difference between these two methods is PUT is idempotent means same request gives same response each time, but post can give different result. 



Friday, 10 May 2024

Caching in ASP.Net Core

Caching is used to stored frequently used data or calculated values to reduce recalculation, fetching data from DB server, which improves performance of application.

ASP.Net Core provides an interface IDistributedCache which exposes APIs to implement caching.

Useful IDistributedCache Methods :

GetString(key) 

GetStringAsync(key) 

SetString(key, value, options)

SetStringAsync(key,value, options)

Refresh(key) 

RefreshAsync(key) 

Remove(key) 

RemoveAsync(key)

Caching service stores data in Key-Value pairs we can set value for a given key and retrieve value by key from Cache.

We can pass options in SetStrings methods which is object of DistributedCacheEntryOptions class.

DistributedCacheEntryOptions class has following useful methods:

AbsoluteExpiration : used to specify an absolute expiry date. 

AbsoluteExpirationRelativeToNow : used to specify a relative expiry date. 

SlidingExpiration : used to specify a period of inactivity, after which the item will be ejected from the cache if it hasn’t been read.

How to Add data to Cache: below code sets (string) key and (string) value in Cache for 2 min.

await cache.SetStringAsync("cacheKey", "String_val_to_Cache" 

 new DistributedCacheEntryOptions { 

 AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(2)

 });

How to Configure Distributed Caching Service: To use distributed cache service we need to configure in Program.cs

builder.Services.AddDistributedMemoryCache(opts => {

 opts.SizeLimit = 200;

});

DistributedMemoryCache is an implementation of  IDistributedCache which is added in service collection by AddDistributedMemoryCache()I will add In-Memory caching functionality to the application. There are two other implementations available using appropriate NuGet packages. 

AddDistributedSqlServerCache: sets up a cache that stores data in SQL Server.                                Nuget: Microsoft.Extensions.Caching.SqlServer

AddStackExchangeRedisCache: This method sets up a Redis cache 
Nuget: Microsoft.Extensions.Caching.Redis 

Note: AddDistributedMemoryCache stores data in Memory so it would not be sharable among other applications.(only it has distributed in it's name:)) 


 AddDistributedSqlServerCache method stores the cache data in a SQL Server database, which can be shared between multiple ASP.NET Core servers and which stores the data persistently.

Steps to use DistributedSqlServerCache:
1. you can create a new Database for caching specific or use your existing database with new table.
2.Need to configure DistributedSqlServerCache similar to DistributedMemotyCache with connection, Database and table details, see the below code:

Adding connection string If, new DB created for caching

"ConnectionStrings": { "CacheConnection": "Server=(localdb)\\MSSQLLocalDB;Database=CachingDb" }

Adding caching service in DI Service collection:

builder.Services.AddDistributedSqlServerCache(opts => { opts.ConnectionString = builder.Configuration["ConnectionStrings:CacheConnection"]
opts.SchemaName = "dbo"; 
 opts.TableName = "DataCache"; });

Inject IDistributedCache in component where you want to use caching.

Note:When you use the IDistributedCache service, the data values are shared between all requests. If you want to cache different data values for each user, then you can use the session middleware. The session middleware relies on the IDistributedCache service to store its data, which means that session data will be stored persistently and be available to a distributed application when the AddDistributedSqlServerCache method is used.


Response Caching
Caching entire response may be a good idea instead of caching individual item, especially in case of UI. Caching responses requires the addition of a service and a middleware component.

Adding services:
builder.Services.AddResponseCaching(); 
builder.Services.AddSingleton(); 

Using Services:
app.UseResponseCaching();

 Note: The response caching feature does not use the IDistributedCache service. Responses are cached in memory and are not distributed.

Example: (copy and paste below code in any editor)

public class TestResponseCaching{ 

 public async Task Endpoint(HttpContext context, IDistributedCache cache, IResponseFormatter formatter, LinkGenerator generator) { 
 int count; 
 int.TryParse((string?)context.Request.RouteValues["count"], out count); 
 long total = 0; 
 for (int i = 1; i <= count; i++) { total += i; } 
 string totalString = $"({ DateTime.Now.ToLongTimeString() }) {total}"; context.Response.Headers["Cache-Control"] = "public, max-age=120";

 string? url = generator.GetPathByRouteValues(context, null, new { count = count }); 
 await formatter.Format(context, $" ({DateTime.Now.ToLongTimeString()}) Total for {count}" + $" values:{totalString} " + $"Reload"); } }
 

Cache-Control header is used to control response caching. The middleware will only cache responses that have a Cache-Control header that contains the public directive. The max-age directive is used to specify the period that the response can be cached for, expressed in seconds.

Response Compressing: ASP.NET Core includes middleware that will compress responses for browsers that have indicated they can handle compressed data. The middleware is added to the pipeline with the UseResponseCompression method. Compression is a trade-off between the server resources required for compression and the bandwidth required to deliver content to the client, and it should not be switched on without testing to determine the performance impact.

Sunday, 5 May 2024

How to use Option pattern in .Net Core

Option pattern allows you to read values from configuration file ( i.e. AppSettings.json) in a strongly typed model.

It's a 4-step process: 

1. add values in AppSettings.json with appropriate section and setting keys.



2.you need to create a class with similar property names, mentioned in AppSettings.json file.

 


3.Need to Configure in Dependency injection container (i.e. need to add in service collection)









4.Finally, we need to add dependency of IOption<MySettings> in Controller constructor to use settings in Controller.









Note: Using IOption will not allow us to change settings in AppSetting.js while application in running mode. we need to restart app to get updated settings.So to overcome this limitation of IOption<T> we can use IOptionSnapShot<T> which will update option model as we change settings in AppSettings.json without restarting application.









Property name in class and keys defined in appsetting must match otherwise you will get null value for the unmatched property. or entire option object null if section and keys are not matched.

Saturday, 4 May 2024

Entity Framework Core : Configuring Entity properties, Primary Key in the DataModel.

 Include and exclude columns from entity: By convention all the public properties with Getter and Setter will be included in the Model. To Exclude any property we can apply [NotMapped] attribute.

public class Blog {

    public int BlogId { get; set; }

    public string Url { get; set; }

    [NotMapped]

    public DateTime LoadedFromDatabase { get; set; }

}

or

protected override void OnModelCreating(ModelBuilder modelBuilder) {

    modelBuilder.Entity<Blog>().Ignore(b => b.LoadedFromDatabase);

}

Column Names Mapping: By Convention Column name will be mapped to the property having same name. we can change this behavior by applying attribute or changing on OnModelCreating().

public class Blog {

    [Column("blog_id")]

   public int BlogId { get; set; }

    public string Url { get; set; }

} 

OR

protected override void OnModelCreating(ModelBuilder modelBuilder) {

    modelBuilder.Entity<Blog>()

                            .Property(b => b.BlogId)

                            .HasColumnName("blog_id");

}

We can also specify datatype of a column in Attribute or using fluent API 

[Column(TypeName = "varchar(200)")]

or using fluent API

eb.Property(b => b.Url).HasColumnType("varchar(200)"); (rest of the code would be similar to above onModelCreating.


Primary Key Configuration

By convention property named 'Id' or <TypeName>Id would be configured as primary key. explicitly we can declare any column as primary key by applying [Key] attribute or in OnModelCreating()

[Key]

public string LicensePlate { get; set; }

OR 

 modelBuilder.Entity<Car>().HasKey(c => c.LicensePlate);

We can also configure composite primary Key like:

modelBuilder.Entity<Car>().HasKey(c => {c.LicensePlate, c.Model} );


Foreign Key Shadow Property

Shadow properties are most often used for foreign key properties, where they are added to the model by convention when no foreign key property has been found by convention or configured explicitly.


Entity Framework Core : Including Excluding Entities, Views, Table Valued Function in the DataModel.

We can include or exclude entity from DataModel using DBSet or OnModelCreating method like:

internal class MyContext : DbContext {

    public DbSet<Blog> Blogs { get; set; }  // Adding via DbSet

    protected override void OnModelCreating(ModelBuilder modelBuilder)  {

        modelBuilder.Entity<AuditEntry>(); // Adding via OnModelCreating; use either way.

    }

}

Note: Entities will be automatically added if declared in Navigation property.


Excluding Entity from Model:

[NotMapped]

public class BlogMetadata {

    public DateTime LoadedFromDatabase { get; set; }

}

or 

protected override void OnModelCreating(ModelBuilder modelBuilder) {

    modelBuilder.Ignore<BlogMetadata>();

}

Excluding Entity from Migration:

protected override void OnModelCreating(ModelBuilder modelBuilder) {

    modelBuilder.Entity<IdentityUser>()

        .ToTable("AspNetUsers", t => t.ExcludeFromMigrations());

}

Mapping Table Name explicitly with Entity:

[Table("blogs")]

public class Blog {

    public int BlogId { get; set; }

    public string Url { get; set; }

}

or

protected override void OnModelCreating(ModelBuilder modelBuilder) {

    modelBuilder.Entity<Blog>()

        .ToTable("blogs");

}

we can also mention schema name in Attribute/fluent API, if needed.

Mapping View with Entity

modelBuilder.Entity<Blog>().ToView("blogsView", schema: "blogging");

NoteMapping to a view will remove the default table mapping, but the entity type can also be mapped to a table explicitly. In this case the query mapping will be used for queries and the table mapping will be used for updates.

Mapping Table Valued Function with Entity

modelBuilder.Entity<BlogWithMultiplePosts>().HasNoKey().ToFunction("BlogsWithMultiplePosts");

NoteIn order to map an entity to a table-valued function the function must be parameterless.



Entity Framework Core - 3 ways to configure/customize entity models in EF Core

 Models can be customized in the entity framework by 3 ways:

  • Conventions
  • DataAnnotations
  • FluentAPI/OnModelCreating

Entity framework by default uses some conventions like, if a model has property with name 'Id' or ClassNameId (i.e. CustomerID) it will set this property as primary key, second way to customize model using 'Data Annotation' (attributes on properties) and third way to customize model on 'OnModelCreating()' event handler/fluent API.

Fluent API configuration has the highest precedence and will override conventions and data annotations.

 Below example show how to configure model on 'OnModelCreating'

internal class MyContext : DbContext {

    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder) {        

            modelBuilder.Entity<Blog>()

            .Property(b => b.Url)

            .IsRequired();

    }

}

Grouping Configuration

Sometimes it's possible that Size of OnModelCreating method is too large, to reduce the size of the OnModelCreating method, all configuration for an entity type can be extracted to a separate class implementing IEntityTypeConfiguration<TEntity>.

We can create a class implementing IEntityTypeConfiguration interface and provide definition to Configure() method. same thing what we were doing on 'OnModelCreating' we will do on Configure() method but it's specific for particular model/entity.

after implementing interface, we need to call Configure method on 'OnModelCreating method'

public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog> {

        public void Configure(EntityTypeBuilder<Blog> builder) {

        builder

            .Property(b => b.Url)

            .IsRequired();

    }

}

Calling Configure method in OnModelCreating:

new BlogEntityTypeConfiguration().Configure(modelBuilder.Entity<Blog>());

Using EntityTypeConfigurationAttribute

Instead calling Configure method for each entity, we can apply EntityTypeConfigurationAttribure on Entity i.e.

[EntityTypeConfiguration(typeof(BookConfiguration))]

public class Book{

    public int Id { get; set; }

    public string Title { get; set; }

    public string Isbn { get; set; }

}

Adding/Removing Convention

You can find default conventions provided by EFCore in the list of classes that implement IConvention interface, you can add your own convention or remove any default convention like:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) {

    configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));

}

Thursday, 2 May 2024

Difference between IActionResult and ActionResult in asp.net core

 Please see the below example to understand difference between IActionResult and ActionResult<T>

For Specific type

public Thing Get() {
    return Context.Things.GetThing(1234);
}

This is OK if the action will always return one possible type. However, most actions may return exceptions (i.e. status codes other than 200) that have different types.

IActionResult type

This solves the above problem the IActionResult return type covers different return types.

public IActionResult Get() {
    Thing thing = Context.Things.GetThing(999);
    if (thing == null)

return NotFound(); else return

else return Ok(thing); }

For asynchronous action, use Task<IActionResult>:

public async Task<IActionResult> Get() {
    Thing thing = await Context.Things.GetThing(1234);
    if (thing == null)
        return NotFound();
    else
        return Ok(thing);
}

ActionResult type

ASP.NET Core 2.1 introduced the ActionResult<T> return type which offers the following benefits over the IActionResult type:

1- The action's expected return type is inferred from the T in ActionResult<T>. If you decorate your action with the [ProducesResponseType] attribute, you no longer need to explicitly specify its Type property. For example, you can simply use [ProducesResponseType(200)] instead of [ProducesResponseType(200, Type = typeof(Thing))].

2- T converts to ObjectResult, which means return new ObjectResult(T); is simplified to return T;.

public ActionResult<Thing> Get() {
    Thing thing = Context.Things.GetThing(1234);
    if (thing == null)
        return NotFound();
    else
        return thing;
}

For asynchronous action, use Task<ActionResult<T>>:

public async Task<ActionResult<Thing>> Get() {
    Thing thing = await Context.Things.GetThing(1234);
    if (thing == null)
        return NotFound();
    else
        return thing;
}

How to create and use middleware in asp.net core

Middleware is piece of code that's assembled into an app pipeline to handle requests and responses.  Each middleware component in the re...