There are 4 rate limiting algorithms:
Fixed window : TheAddFixedWindowLimiter
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.I have written some articles on asp.net core mvc. which is useful in our day to day programming with asp,net core 2.0 and above
There are 4 rate limiting algorithms:
Fixed window : TheAddFixedWindowLimiter
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.To use Redis for distributed caching in ASP.Net core you need to follow below steps:
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();
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.
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:
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.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.
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.
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.
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");
Note: Mapping 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");
Note: In order to map an entity to a table-valued function the function must be parameterless.
Models can be customized in the entity framework by 3 ways:
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));
}
Please see the below example to understand difference between IActionResult and ActionResult<T>
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.
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);
}
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;
}
Middleware is piece of code that's assembled into an app pipeline to handle requests and responses. Each middleware component in the re...