Entity Framework With Mysql

packages you have to install

         <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.5" />
        <PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="7.0.5" />
        <PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="7.0.5" />
        <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.5">
          <PrivateAssets>all</PrivateAssets>
          <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
        <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />

Create Tables Classes

assume we have two tables like this

Students table
Courses table

then we will build class or record for it like

PrimaryKey("StudentId")]
public class Student
{
    public string StudentId { get; set; }
    public string? StudentName { get; set; }
    public string? cid { get; set; }
    public Course? Course { get; set; }
}

[PrimaryKey("CourseId")]
public class Course
{
    
    public string CourseId { get; set; }
    [Required]
    public string? CourseName { get; set; }
    //public ICollection<Student> Students { get; set; }
}


build the context

public class SchoolContext : DbContext
{
    public DbSet<Student> Students { get; set; }
    public DbSet<Course> Courses { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
              // configure connection
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseMySql($"server=127.0.0.1;user=school;password=*******;database=FoodMenu",
                MySqlServerVersion.AutoDetect("server=127.0.0.1;user=schhol;password=*******;database=FoodMenu"));
        }
    }
    
    // build relations 
    protected sealed override void OnModelCreating(ModelBuilder modelBuilder)
    {

        modelBuilder.Entity<Student>().HasOne<Course>(s => s.Course).WithMany().HasForeignKey("cid");
        //modelBuilder.Entity<Course>().HasMany<Student>(s => s.Students).WithOne(s=>s.Course).HasForeignKey((e)=>e.CourseId);
    }
}

then you can use it directly

using(SchoolContext context = new SchoolContext())
{
    var res2= context.Students.Include(s=>s.Course).ToList();
    foreach (var item in res2)
    {
        Console.WriteLine(JsonSerializer.Serialize(item).ToString());
    }
}

or use it as a service


builder.Services.AddDbContext<SchoolContext>(c =>
{
    c.UseMySql("connection string",  MySqlServerVersion.AutoDetect("connection string"));
});

ASP.NET Core – Dependency Injection and Middlewares

Services

assume we have logger service like

public interface ILog
{
    void info(string str);
}

class MyConsoleLogger : ILog
{
    public void info(string str)
    {
        Console.WriteLine(str);
    }
}

then we can register it in ioc like


builder.Services.Add(new ServiceDescriptor(typeof(ILog), typeof( MyConsoleLogger)));

in the above example the service it singleton by default

  1. Singleton: IoC container will create and share a single instance of a service throughout the application’s lifetime.
  2. Transient: The IoC container will create a new instance of the specified service type every time you ask for it.
  3. Scoped: IoC container will create an instance of the specified service type once per request and will be shared in a single request.
public void ConfigureServices(IServiceCollection services)
{
    services.Add(new ServiceDescriptor(typeof(ILog), new MyConsoleLogger()));    // singleton
    
    services.Add(new ServiceDescriptor(typeof(ILog), typeof(MyConsoleLogger), ServiceLifetime.Transient)); // Transient
    
    services.Add(new ServiceDescriptor(typeof(ILog), typeof(MyConsoleLogger), ServiceLifetime.Scoped));    // Scoped
}

access to service

var services = this.HttpContext.RequestServices;
var log = (ILog)services.GetService(typeof(ILog));
// or in controller api parameter you can access it by 
[FromServices] IReCaptcha reCaptcha

asp.net core Identity and authentication

HttpContext

HttpContext encapsulates all information about an individual HTTP request and response.
and it capsulated by ControllerBase

HttpContext.User it will represent Th Identity any object implement System.Security.Principal.IIdentity and it wrapped by Principal Class Object 
HttpContext Can Access to Any WebApplication Service by RequestServices.GetService<>() .... 

Identity 
It is Group OF Claims each claim represent kind of user data
most two famous Identity is ClaimsIdentinty And Generic Identity

Principal
it will Cover the Identity and provide you utilities to check if is there claim or schema or policy ... etc 

protected async override sealed Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        
        var authorizationHeader = Request.Headers["Authorization"].ToString();
        if (authorizationHeader != null && authorizationHeader.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
        {
            var token = authorizationHeader.Substring("Basic ".Length).Trim();
            var credentialsAsEncodedString = Encoding.UTF8.GetString(Convert.FromBase64String(token));
            var credentials = credentialsAsEncodedString.Split(':');
            try
            {
                User? user = await Users.Login(credentials[0], credentials[1]);
                var identity = user;
                List<Claim> claims = new() { new Claim("uid", user.user_info.uid), new Claim("token", user.user_info.token) };
                foreach (var authorization in user.user_info.authorization)
                {
                    claims.Add(new Claim(authorization.ToString("G"), "true"));
                }
                this.Context.Items.Add("user", new ClaimsPrincipal(identity));
                ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(claims,"Basic"));
                return await Task.FromResult(
                    AuthenticateResult.Success(new AuthenticationTicket(principal, "Basic")));

            }
            catch (Exception e)
            {
                Response.StatusCode = 401;
                Response.Headers.Add("WWW-Authenticate", "Basic realm=\"thesmartcircuit.com\"");
                return await Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header"));
            }
        }

        Response.StatusCode = 401;
        Response.Headers.Add("WWW-Authenticate", "Basic realm=\"thesmartcircuit.com\"");
        return await Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header"));
    }

then you can add new authoentication to this services of web application .

builder.Services.AddAuthentication("Basic")
    .AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>("Basic", null);

to apply this authentication on any minimal api


app.MapGet("/", () => "Hello World!").RequireAuthorization((b) => { b.RequireClaim("admin");});
//or 
[Authorize(AuthenticationSchemes = "Basic")]

Authorizations And Policies
add authorization service with Admin policy

builder.Services.AddAuthorizationBuilder().AddPolicy("Admin", (pb) =>
{
    pb.RequireAuthenticatedUser().AddAuthenticationSchemes("Basic").RequireRole("admin");
});

then you can use this authorized policy with minimal api like

[Authorize(Policy = "User")]

Authentication Handler
there are already built in auth handler service like cookies and ODB if you like to customize new you can inherit AuthenticationHandler

add roles

new Claim(ClaimTypes.Role,authorization.ToString("G"));
//or instean of using ClaimTypes.Role you can identify any string as role type by add it to roletype parameter in  ClaimsIdentity