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.


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...