C# is a topic the MagnusMinds engineering team writes about from project work: how it is set up, where it breaks, and what we changed in production. This tag page lists all 10 articles tagged C#.
Start with “Exploring the Latest Features in C# 10”, “Secure Authentication and Authorization in .NET Core”, or “Mastering Dependency Injection in .NET Core Applications”. Open a post for the full write-up, or follow another tag or category on the card to a related topic.
Exploring the Latest Features in C# 10 C# 10, the latest version of the C# programming language, brings exciting new features and enhancements that aim to improve developer productivity, code readability, and overall language capabilities. In this blog post, we'll take a closer look at some of the notable features introduced in C# 10. 1. Record Types Improvements Record types, introduced in C# 9, have proven to be a valuable addition for simplifying immutable data structures. In C# 10, record types receive enhancements that make them even more powerful. Example: csharpCopy code public record Person { public string FirstName { get; init; } public string LastName { get; init; } } // C# 10 allows you to simplify the instantiation of record types var person = new Person { FirstName = "John", LastName = "Doe" }; In C# 10, you can use the with expression to create a copy of a record with modified values more concisely: csharpCopy code var updatedPerson = person with { FirstName = "Jane" }; This syntax improves the readability of code when updating record instances. 2. Parameter Null Checking C# 10 introduces the notnull modifier for parameters, enabling developers to enforce non-null arguments at the call site. This can lead to more robust code by catching potential null reference exceptions early. Example: public void ProcessData(notnull string data) { // The 'data' parameter is guaranteed to be non-null within this method } The notnull modifier serves as a contract, making it clear that the method does not accept null arguments. 3. Global Usings C# 10 simplifies the process of importing namespaces by introducing global usings. Now, you can include common namespaces globally, reducing the need to include them in each file. Example: // C# 10 global using global using System; global using System.Collections.Generic; class Program { static void Main() { List<string> myList = new(); // ... } } By using global usings, you can make your code more concise and improve overall code readability. 4. File-scoped Namespace Declarations C# 10 introduces file-scoped namespace declarations, allowing you to define namespaces directly at the file level. This simplifies the organization of code and reduces the need for excessive indentation. Example: csharpCopy code // File-scoped namespace declaration namespace MyNamespace; class MyClass { // ... } This feature promotes cleaner code structure, making it easier to navigate and maintain. 5. Extended Property Patterns C# 10 enhances property patterns, enabling more expressive and concise matching when working with switch statements and patterns. Example: csharpCopy code public class Point { public int X { get; set; } public int Y { get; set; } } var point = new Point { X = 5, Y = 10 }; // C# 10 extended property patterns var result = point switch { { X: 0, Y: 0 } => "Origin", { X: var x, Y: var y } when x == y => "Diagonal", _ => "Unknown" }; These improvements make pattern matching even more powerful and expressive. Conclusion C# 10 introduces several features and enhancements that aim to make the language more expressive, concise, and developer-friendly. By leveraging these new capabilities, developers can write cleaner, more maintainable code. As always, staying updated with the latest language features empowers developers to make the most of the tools at their disposal. Explore these features in your projects and embrace the evolution of C# for a more enjoyable and efficient development experience.
Authentication and authorization are essential components of any web application, ensuring the security and proper access control for users. In NET Core, these concepts play a crucial role in protecting resources and determining user permissions. Authentication in NET Core Authentication is the process of verifying the identity of a user, ensuring they are who they claim to be. This is typically done by presenting credentials, such as a username and password, and validating them against a trusted source, such as a database or an external authentication provider. Once authenticated, the user is assigned an identity, which is then used for subsequent authorization checks. Authentication in NET Core Authentication in NET Core revolves around the concept of authentication schemes. An authentication scheme represents a specific method or protocol used to authenticate users. NET Core supports various authentication schemes out of the box, including cookie authentication, JWT bearer authentication, and external authentication providers like OAuth and OpenID Connect. Understanding Authentication Schemes Authentication schemes are registered in the application’s startup class using the AddAuthentication method. This method allows you to specify one or more authentication schemes and their respective options. For example, to enable cookie authentication, you can use the AddCookie services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { // Configure CookieAuthenticationDefaults options }); Configuring Cookie Authentication To configure cookie authentication, you need to specify the authentication scheme as CookieAuthenticationDefaults.AuthenticationScheme and provide the necessary options, such as the cookie name, login path, and authentication endpoint. Here's an example: services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.Cookie.Name = "MyCookie"; options.LoginPath = "/Admin/Login"; }); In this example, the cookie authentication middleware is configured to use the scheme named “MyCookie” and redirect users to the “/Admin/Login” page if they try to access a protected resource without being authenticated. The options object allows you to customize various aspects of cookie authentication, such as cookie expiration and sliding expiration. Implementing Claim-Based Authentication A claim represents a piece of information about the user, such as their name, email address, or role. By using claims, you can easily extend the user’s identity with additional data and make authorization decisions based on these claims. In NET Core, claim-based authentication is implemented using the ClaimsIdentity and ClaimsPrincipal classes. The ClaimsIdentity represents a collection of claims associated with a user, while the ClaimsPrincipal represents the user's identity as a whole. When a user is authenticated, their claims are stored in a ClaimsPrincipal, which is then attached to the current request's HttpContext.User property. To implement claim-based authentication, you need to create and populate a ClaimsIdentity object with the relevant claims. This can be done during the authentication process, typically in a custom authentication handler. Here's an example of how to create a ClaimsIdentity with a username claim: var claims = new List<Claim> { new Claim(ClaimTypes.Name, "Himanshu") }; var identity = new ClaimsIdentity(claims, "MyAuthenticationScheme"); var principal = new ClaimsPrincipal(identity); await HttpContext.SignInAsync(principal); External Authentication Providers External authentication allows users to sign in to your application using their existing accounts from popular platforms like Google, Facebook, Twitter, and Microsoft. To enable external authentication, you need to configure the desired authentication provider and register it in your application’s startup class. services.AddAuthentication() .AddGoogle(options => { options.ClientId = "YOUR_GOOGLE_CLIENT_ID"; options.ClientSecret = "YOUR_GOOGLE_CLIENT_SECRET"; }); Securing APIs with JWT Bearer Authentication .NET Core provides built-in support for securing APIs using JSON Web Tokens (JWT) and the JWT bearer authentication scheme. JWTs are self-contained tokens that contain information about the user and their permissions. By validating the integrity and authenticity of a JWT, you can trust the claims it contains and authenticate API requests. To enable JWT bearer authentication, you need to configure the authentication scheme and provide the necessary options, such as the token validation parameters and the issuer signing key. Here’s an example of configuring JWT bearer authentication: services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateIssuerSigningKey = true, ValidIssuer = "YOUR_ISSUER", ValidAudience = "YOUR_AUDIENCE", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("YOUR_SIGNING_KEY")) }; }); In this example, the AddJwtBearer extension method is used to configure JWT bearer authentication. The TokenValidationParameters object is set with the necessary validation rules, such as validating the issuer, audience, and the issuer's signing key. You need to replace the placeholder values with your own values specific to your JWT setup. With JWT bearer authentication enabled, API endpoints can be protected by applying the [Authorize] attribute to the corresponding controller or action. This ensures that only requests with valid and authenticated JWTs are allowed access to the protected resources. Maintain secure Authorization Authorization in NET Core is primarily controlled through the use of the [Authorize] attribute. This attribute can be applied at the controller or action level to restrict access to specific components of your application. By default, the [Authorize] attribute allows only authenticated users to access the protected resource. The Role of Authorize Attribute : For example, you can use the [Authorize(Roles = "Admin")] attribute to restrict access to administrators only. This ensures that only users with the "Admin" role can access the protected resource. Restricting Access with Policies : While the [Authorize] attribute provides a simple way to restrict access, ASP.NET Core also supports more advanced authorization policies. Authorization policies allow you to define fine-grained rules for determining whether a user is authorized to perform a specific action. To use authorization policies, you need to define them in your application’s startup class using the AddAuthorization method. Here's an example: services.AddAuthorization(options => { options.AddPolicy("AdminOnly", policy => { policy.RequireRole("Admin"); }); }); Rrole-based authorization can be implemented using the built-in role-based authentication system or by integrating with an external identity provider, such as Active Directory or Azure AD. Implementing Two-Factor Authentication Two-factor authentication (2FA) adds an extra layer of security to the authentication process by requiring users to provide additional verification, typically in the form of a one-time password or a biometric factor. Implementing 2FA can significantly reduce the risk of unauthorized access, especially for sensitive applications or those handling confidential information. To implement two-factor authentication, you need to configure the desired authentication providers, such as SMS, email, or authenticator apps, and register them in your application’s startup class. You also need to configure the necessary options, such as the message templates or the issuer signing key. By enabling two-factor authentication, you provide an additional layer of security that can help protect user accounts from unauthorized access, even if their credentials are compromised. Protecting Against Common Security Vulnerabilities When implementing authentication and authorization in your application, it’s crucial to be aware of common security vulnerabilities and take appropriate measures to prevent them. By understanding these vulnerabilities and following security best practices, you can ensure the integrity and confidentiality of user data. Some common security vulnerabilities to consider when implementing authentication and authorization include: Cross-Site Scripting (XSS): Protect against XSS attacks by properly encoding user input and validating data before rendering it in HTML or JavaScript. Cross-Site Request Forgery (CSRF): Implement CSRF protection mechanisms, such as anti-forgery tokens, to prevent attackers from executing unauthorized actions on behalf of authenticated users. Brute-Force Attacks: Implement account lockout policies and rate limiting to protect against brute-force attacks that attempt to guess user credentials. Session Management: Use secure session management techniques, such as session timeouts, secure cookie attributes, and session regeneration, to prevent session hijacking or session fixation attacks. Password Storage: Store passwords securely by using strong hashing algorithms, salting, and iteration counts to protect against password cracking attempts. By addressing these vulnerabilities and following security best practices, you can minimize the risk of unauthorized access, data breaches, and other security incidents. Conclusion: Authentication and authorization are critical components of building secure and robust web applications in .Net Core. By understanding the concepts and leveraging the powerful features provided by. NET Core, developers can implement robust security measures to protect their applications and ensure that users access resources securely and efficiently.
Dependency Injection (DI) is a core design pattern in .NET Core, enabling developers to build flexible, maintainable, and testable applications. By decoupling the creation and management of dependencies from the business logic, DI helps create loosely coupled systems that are easier to manage and evolve. This blog will guide you through mastering Dependency Injection in .NET Core applications, covering the basics to advanced usage. What is Dependency Injection? Dependency Injection is a design pattern where an object receives its dependencies from an external source rather than creating them itself. In simpler terms, it allows objects to be injected with their required dependencies, promoting loose coupling and enhancing testability. Types of Dependency Injection: Constructor Injection: Dependencies are provided through a class constructor. Property Injection: Dependencies are set through public properties. Method Injection: Dependencies are passed through method parameters. Why Use Dependency Injection? Loose Coupling: Reduces dependencies between components, making them easier to manage and test. Enhanced Testability: Mock dependencies can be easily injected, facilitating unit testing. Flexibility: Allows for easy swapping of implementations without modifying the dependent classes. Configuration: Centralizes configuration for object creation, making it easier to manage. Implementing Dependency Injection in .NET Core In .NET Core, the DI framework is built-in and tightly integrated with the framework, making it easy to use in any .NET Core application. 1. Registering Services Services are registered in the ConfigureServices method in the Startup.cs file. The framework provides three lifetimes for service registration: Transient: A new instance is created every time the service is requested. Scoped: A new instance is created per request. Singleton: A single instance is created and shared throughout the application's lifetime. public void ConfigureServices(IServiceCollection services) { services.AddTransient<IMyService, MyService>(); // Transient services.AddScoped<IMyService, MyService>(); // Scoped services.AddSingleton<IMyService, MyService>(); // Singleton } 2. Injecting Services Once registered, services can be injected into controllers, services, or any other classes via constructor injection. public class MyController : Controller { private readonly IMyService _myService; public MyController(IMyService myService) { _myService = myService; } public IActionResult Index() { var result = _myService.DoSomething(); return View(result); } } 3. Using DI in Middleware Middleware components in the request pipeline can also use Dependency Injection. public class MyMiddleware { private readonly RequestDelegate _next; private readonly IMyService _myService; public MyMiddleware(RequestDelegate next, IMyService myService) { _next = next; _myService = myService; } public async Task InvokeAsync(HttpContext context) { _myService.DoSomething(); await _next(context); } } Register the middleware in the Configure method: public void Configure(IApplicationBuilder app) { app.UseMiddleware<MyMiddleware>(); } Advanced Scenarios: 1. Conditional Dependency Resolution You can conditionally resolve dependencies using IServiceProvider or IHttpContextAccessor for scenarios where the dependency may vary based on context. public class MyService : IMyService { private readonly IAnotherService _anotherService; public MyService(IServiceProvider serviceProvider) { _anotherService = serviceProvider.GetService<IAnotherService>(); } } 2. Service Lifetime Management Understanding service lifetimes is crucial, especially when mixing services with different lifetimes. Singleton services should not capture scoped or transient dependencies as it can cause memory leaks or unexpected behavior. Scoped services should avoid holding transient dependencies beyond the request scope. 3. Using the Options Pattern The Options pattern is a technique for handling configuration in .NET Core using DI. It allows you to register and configure POCOs as services. public class MyOptions { public string Option1 { get; set; } } public void ConfigureServices(IServiceCollection services) { services.Configure<MyOptions>(Configuration.GetSection("MyOptions")); } public class MyService : IMyService { private readonly MyOptions _options; public MyService(IOptions<MyOptions> options) { _options = options.Value; } } Best Practices for Dependency Injection Avoid Service Locator Pattern: Using IServiceProvider excessively is considered an anti-pattern as it hides dependencies. Favor Constructor Injection: It makes dependencies explicit and promotes immutability. Register Interfaces, Not Implementations: Register interfaces or abstract classes to decouple the implementation from the interface. Keep Services Small and Focused: Adhere to the Single Responsibility Principle (SRP) to ensure services do one thing well. Conclusion Mastering Dependency Injection in .NET Core applications is a key skill for any .NET developer. By understanding the different types of DI, how to implement it, and best practices, you can build applications that are more modular, testable, and maintainable. Whether you’re just starting with DI or looking to deepen your understanding, the concepts and techniques covered in this blog will help you harness the full power of Dependency Injection in your .NET Core projects.
In the world of software development and testing, having access to realistic and diverse data sets is crucial. That's why we are thrilled to introduce IndiGen, a powerful and versatile package designed to generate realistic Indian data with ease. Why IndiGen? IndiGen is a comprehensive tool that caters specifically to the needs of developers and testers who require authentic Indian data for their projects. Whether you are working on unit tests, creating sample data, or validating functionality, IndiGen has got you covered. Key Features Realistic Indian Names: Generate complete names, first names, last names, middle names, prefixes, and suffixes. var fullName = India.Faker.Name.FullName(); // Example: Ramesh Babu var firstName = India.Faker.Name.First(); // Example: Amitabh var lastName = India.Faker.Name.Last(); // Example: Kapoor var middleName = India.Faker.Name.Middle(); // Example: Hrutvik var prefix = India.Faker.Name.Prefix(); // Example: Shri var suffix = India.Faker.Name.Suffix(); // Example: Bhai, Kumar Valid Phone Numbers: Generate realistic Indian phone numbers. var phoneNumber = India.Faker.Phone.Number(); // Example: +91-9988776655, 9998887770, 079-27474747 Authentic Vehicle Number Plates: Generate vehicle number plates in Indian formats. var vehicleNumberPlate = India.Faker.VehicleNumberPlate.Number(); // Example: GJ 01 AA 7777, 24 BH 9999 AA Valid PAN Card Numbers: Generate PAN card numbers that conform to Indian standards. var panCardNumber = India.Faker.PanCardNumber.Number(); // Example: AABBB8888A Aadhaar Card Numbers: Generate Aadhaar card numbers. var aadhaarCardNumber = India.Faker.AadharCardNumber.Number(); // Example: 2222 4444 2222 Supported Versions IndiGen is compatible with a wide range of .NET versions, ensuring flexibility and ease of integration into your projects: .NET Framework 4.5, 4.6, 4.7, 4.8 .NET Standard 2.0, 2.1 .NET Core 3.0, 3.1 .NET 5.0, 6.0 Get Started with IndiGen Getting started with IndiGen is simple. Visit our NuGet package page and integrate it into your projects to start generating realistic Indian data today. How to Install Installing IndiGen is straightforward. You can add it to your project using the NuGet Package Manager, .NET CLI, or by editing your project file. Using NuGet Package Manager Open your project in Visual Studio. Go to Tools > NuGet Package Manager > Manage NuGet Packages for Solution. Search for IndiGen. Select the package and click Install. Using .NET CLI Run the following command in your terminal:\ dotnet add package IndiGen Editing Your Project File Add the following line to your .csproj file: <PackageReference Include="IndiGen" Version="8.0.1" /> Replace "8.0.1" with the latest version of IndiGen. NuGet Package: IndiGen Contribute to IndiGen We welcome contributions from the community. If you have suggestions, improvements, or new features in mind, please open an issue or submit a pull request. Together, we can make IndiGen even better! IndiGen is here to simplify your development and testing process by providing realistic Indian data. Try it out and let us know your thoughts. Happy coding!
Introduction In the ever-evolving landscape of web development, simplicity is key. Enter Minimal APIs in ASP.NET Core, a lightweight and streamlined approach to building web applications. In this detailed blog, we'll explore the concept of Minimal APIs, understand why they matter, and walk through their implementation in ASP.NET Core. When to Use Minimal APIs? Minimal APIs are well-suited for small to medium-sized projects, microservices, or scenarios where a lightweight and focused API is sufficient. They shine in cases where rapid development and minimal ceremony are top priorities. You can find in this blog <link> how to create minimal api. I am directly showing the comparison between MinimalAPI and controller. Controllers: Structured and Versatile Controllers, deeply rooted in the MVC pattern, have been a cornerstone of ASP.NET API development for years. They provide a structured way to organize endpoints, models, and business logic within dedicated controller classes. Let's consider an example using Microsoft.AspNetCore.Mvc; namespace MinimalAPI.Controllers { [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<WeatherForecast> Get() { return 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(); } } } Advantages of Controllers in Action Structure and Organization: Controllers offer a clear structure, separating concerns and enhancing maintainability. Flexibility: They enable custom routes, complex request handling, and support various HTTP verbs. Testing: Controllers facilitate unit testing of individual actions, promoting a test-driven approach Minimal APIs: Concise and Swift With the advent of .NET 6, Minimal APIs emerged as a lightweight alternative, aiming to minimize boilerplate code and simplify API creation. Here's an example showcasing Minimal APIs. using MinimalAPI; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); app.MapGet("/GetWeatherForecast", () => { var rng = new Random(); var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; var weatherForecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index).Date, TemperatureC = rng.Next(-20, 55), Summary = summaries[rng.Next(summaries.Length)] }).ToArray(); return Results.Ok(weatherForecasts); }); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); Advantages of Minimal APIs in Focus Simplicity: Minimal APIs drastically reduce code complexity, ideal for smaller projects or rapid prototyping. Ease of Use: They enable quick API creation with fewer dependencies, accelerating development cycles. Potential Performance Boost: The reduced overhead might lead to improved performance, especially in smaller applications. What you choose between MinimalAPI and Controller? Choosing between Controllers and Minimal APIs hinges on various factors. Project Scale: Controllers offer better organization and structure for larger projects with intricate architectures. Development Speed: Minimal APIs shine when speed is crucial, suitable for rapid prototyping or smaller projects. Team Expertise: Consider your team's familiarity with MVC patterns versus readiness to adopt Minimal APIs. Conclusion The decision between Controllers and Minimal APIs for .NET APIs isn't about one being superior to the other. Rather, it's about aligning the choice with the project's specific needs and constraints. Controllers offer robustness and versatility, perfect for larger, complex projects. On the other hand, Minimal APIs prioritize simplicity and rapid development, ideal for smaller, more straightforward endeavours.
What is Authorization? Authorization verifies whether a user has permission to use specific applications or services. While authentication and authorization are distinct processes, authentication must precede authorization, ensuring the user's identity is confirmed before determining their access rights. When logging into a system, a user must provide credentials like a username and password to authenticate. Next, the authorization process grants rights. For example, an administrative user can create a document library to add, edit, and delete documents, while a non-administrative user can only read documents in the library. Types of Authorization: Simple Authorization Role-Based Authorization Claim-Based Authorization Policy-Based Authorization I have implemented an example of role-based authorization in .NET. Step 1: Create one new MVC Web Application with the Authentication type “Individual Account”. Step 2: Register Identity with DefaultTokenProvider in the program.cs file. builder.Services.AddIdentity<IdentityUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = false) .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); For better understanding, I have added one page to add a new role. Create a new method in the controller and add the following code. [HttpGet] public IActionResult Admin() { return View(); } Create a new model Role.cs. namespace Authorization.Models { public class Role { public string RoleName { get; set; } } } Create html page for add role. @model Role @{ ViewData["Title"] = "Admin"; } <h1>Admin</h1> <div class="row"> <div class="col-md-12"> <form method="post" action="@Url.Action("Admin","Home")"> <div class="form-group"> <label>Role Name</label> <input type="text" class="form-control" style="width:30%;" asp-for="RoleName" placeholder="Role name" required> </div> <br /> <button class="btn btn-success" type="submit">Add</button> </form> </div> </div> I have created a simple page, You can modify the page as per your requirements. Add a new method in the controller and add the following code. And declare RoleManager<IdentityRole> and inject in the constructor. private readonly RoleManager<IdentityRole> _roleManager; public HomeController(RoleManager<IdentityRole> roleManager) { _roleManager = roleManager; } [HttpPost] public async Task<IActionResult> Admin(Role role) { var result = _roleManager.RoleExistsAsync(role.RoleName).Result; if (!result) { await _roleManager.CreateAsync(new IdentityRole(role.RoleName)); } return RedirectToAction("Admin"); } Set a new tab in _Layout.cshtml file to redirect to the Add role page. <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Admin">Add new role</a> </li> Run the project and you will see the output. Here, You can add a new role. Add a new field in register.cshtml using the following code to assign a role to the user. <div class="form-floating mb-3"> <select asp-for="Input.Role" class="form-control" aria-required="true"> <option value="">Select role</option> @foreach (var item in Model.RoleList) { <option value="@item.Name">@item.Name</option> } </select> <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span> </div> To get the list of roles you can add the following code in your register.cshtml.cs file. And Add RoleList = _roleManager.Roles in OnPostAsync method also. public IQueryable<IdentityRole> RoleList { get; set; } public async Task OnGetAsync(string returnUrl = null) { ReturnUrl = returnUrl; RoleList = _roleManager.Roles; ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); } Run the project and see the output. Now, Assign the role to the user, and for that add the following code in OnPostAsync after the user is created. await _userManager.AddToRoleAsync(user, Input.Role); Full code of register.cshtml.cs file. using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.WebUtilities; using System.ComponentModel.DataAnnotations; using System.Text; namespace Authorization.Areas.Identity.Pages.Account { public class RegisterModel : PageModel { private readonly SignInManager<IdentityUser> _signInManager; private readonly UserManager<IdentityUser> _userManager; private readonly RoleManager<IdentityRole> _roleManager; private readonly IUserStore<IdentityUser> _userStore; private readonly IUserEmailStore<IdentityUser> _emailStore; private readonly ILogger<RegisterModel> _logger; //private readonly IEmailSender _emailSender; public RegisterModel( UserManager<IdentityUser> userManager, IUserStore<IdentityUser> userStore, SignInManager<IdentityUser> signInManager, ILogger<RegisterModel> logger, RoleManager<IdentityRole> roleManager ) { _userManager = userManager; _userStore = userStore; _emailStore = GetEmailStore(); _signInManager = signInManager; _roleManager = roleManager; _logger = logger; } [BindProperty] public InputModel Input { get; set; } public IQueryable<IdentityRole> RoleList { get; set; } public string ReturnUrl { get; set; } public IList<AuthenticationScheme> ExternalLogins { get; set; } public class InputModel { [Required] [EmailAddress] [Display(Name = "Email")] public string Email { get; set; } [Required] [Display(Name = "Role")] public string Role { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm password")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] public string ConfirmPassword { get; set; } } public async Task OnGetAsync(string returnUrl = null) { ReturnUrl = returnUrl; RoleList = _roleManager.Roles; ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); } public async Task<IActionResult> OnPostAsync(string returnUrl = null) { returnUrl ??= Url.Content("~/"); RoleList = _roleManager.Roles; ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); if (ModelState.IsValid) { var user = CreateUser(); await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); var result = await _userManager.CreateAsync(user, Input.Password); if (result.Succeeded) { _logger.LogInformation("User created a new account with password."); await _userManager.AddToRoleAsync(user, Input.Role); var userId = await _userManager.GetUserIdAsync(user); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); if (_userManager.Options.SignIn.RequireConfirmedAccount) { return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl }); } else { await _signInManager.SignInAsync(user, isPersistent: false); return LocalRedirect(returnUrl); } } foreach (var error in result.Errors) { ModelState.AddModelError(string.Empty, error.Description); } } return Page(); } private IdentityUser CreateUser() { try { return Activator.CreateInstance<IdentityUser>(); } catch { throw new InvalidOperationException($"Can't create an instance of '{nameof(IdentityUser)}'. " + $"Ensure that '{nameof(IdentityUser)}' is not an abstract class and has a parameterless constructor, or alternatively " + $"override the register page in /Areas/Identity/Pages/Account/Register.cshtml"); } } private IUserEmailStore<IdentityUser> GetEmailStore() { if (!_userManager.SupportsUserEmail) { throw new NotSupportedException("The default UI requires a user store with email support."); } return (IUserEmailStore<IdentityUser>)_userStore; } } } Now, register one new user and assign a role to them. For example, I have created one user and assign “Admin” to them. I have added two new methods in the controller and added a default view for that. [Authorize(Roles = "User")] public IActionResult UserRoleCheck() { return View(); } [Authorize(Roles = "Admin")] public IActionResult AdminRoleCheck() { return View(); } I have set the Authorize attribute with the role name on both authorization methods. Now, I am running the project and clicking on Admin Role, It will open the page of admin because the logged user role and method role both are the same. If I click on User Role, It will give an Access denied error. Because logged user role and method role both are different. Here, I am using by default access denied page of identity. You can use custom page also, Just set this path to program.cs file. builder.Services.ConfigureApplicationCookie(options => { options.AccessDeniedPath = "/Identity/Account/AccessDenied"; // Customize this path as per your application's structure }); Using this way you will implement the role-based authorization in your application. Conclusion By properly implementing authorization in your applications, you can ensure that resources and sensitive information are accessible only to authorized users. Remember to choose the appropriate authorization technique based on your application’s requirements and complexity.
In this blog, I will explore the top useful C# .NET snippets that every developer should have in their arsenal. From object initialization syntax to dictionary initialization, these snippets cover a wide range of functionalities that will help you streamline your C# development process. Object Initialization Syntax - By using object initialization syntax, you can quickly create and initialize objects without the need for multiple lines of code. public class Product { public string Name { get; set; } public decimal Price { get; set; } } var product = new Product { Name = "Mouse", Price = 999.00 }; Enumerable.Range Method - This snippet simplifies the process of iterating over a range of numbers in a concise and readable manner. foreach (var number in Enumerable.Range(1, 10)) { Console.WriteLine(number); } Conditional Ternary Operator - By using the conditional ternary operator, you can streamline conditional checks and make your code more compact and readable. int time = 7; var result = (time < 5) ? "Weekend" : "Working"; Console.WriteLine(result); Task.WhenAll Method - With Task.WhenAll, you can improve the performance of your asynchronous operations by running them concurrently. async Task DownloadAllAsync(List<string> urls) { var tasks = urls.Select(url => DownloadAsync(url)).ToArray(); await Task.WhenAll(tasks); } async Task DownloadAsync(string url) { Console.WriteLine($"Downloading from {url}"); } Null-Conditional Operator - By using the null-conditional operator, you can handle null values gracefully and prevent runtime exceptions in your code. string firstName = person?.FirstName ?? "Unknown"; Console.WriteLine(firstName); LINQ Query Syntax - By leveraging LINQ query syntax, you can write complex queries on collections with ease and readability. var scores = new int[] { 90, 100, 82, 89, 92 }; var highScores = from score in scores where score >= 90 select score; foreach (var score in highScores) { Console.WriteLine(score); } Using Statement - The using statement is essential for handling disposable objects and preventing resource leaks in your code. using (var streamReader = new StreamReader(@"C:\file.txt")) { string content = streamReader.ReadToEnd(); Console.WriteLine(content); } Expression-Bodied Members - By using expression-bodied members, you can make your code more concise and expressive, especially for simple properties and methods. public class Person { public string FirstName { get; set; } public string LastName { get; set; } public string FullName => $"{FirstName} {LastName}"; // Fullname directly dynamically set at model level. } Dictionary Initialization - Dictionary initialization simplifies the process of populating key-value pairs in a dictionary with a clean and readable syntax. var capitals = new Dictionary<string, string> { ["USA"] = "Washington, D.C.", ["Japan"] = "Tokyo", ["India"] = "Delhi" }; Appending an Element to a List - C# offers numerous methods for adding items to lists. For instance, the widely-used Add() method is available. However, there are plenty of other options as well. Here are five: // Statically defined list List<int> myList = new List<int> {2, 5, 6}; // Appending using Add() myList.Add(5); // [2, 5, 6, 5] // Appending using AddRange() myList.AddRange(new List<int> {9}); // [2, 5, 6, 5, 9] // Appending using Insert() myList.Insert(myList.Count, -4); // [2, 5, 6, 5, 9, -4] // Appending using InsertRange() myList.InsertRange(myList.Count, new List<int> {3}); // [2, 5, 6, 5, 9, -4, 3] // To Check if a List Is Empty List<int> myList = new List<int>(); // Check if a list is empty by its Count if (myList.Count == 0) { // the list is empty } // Check if a list is empty by its type flexibility **preferred method** if (!myList.Any()) { // the list is empty } String Interpolation (Formatting a String) - Oftentimes, we need to format strings to display information in a more readable or structured manner. Here are some options: string name = "Himanshu"; int age = 25; // String formatting using concatenation Console.WriteLine("My name is " + name + ", and I am " + age + " years old."); // String formatting using composite formatting Console.WriteLine("My name is {0}, and I am {1} years old.", name, age); // String formatting using interpolation (C# 6.0+) Console.WriteLine($"My name is {name}, and I am {age} years old"); These are but a small example of the power and flexibility that C# and .NET bring to the table.Thank you for reading! We hope these C# .NET snippets will help you streamline your development process and boost your productivity.
What is MinimalAPI? Minimal APIs are a simplified way of building web APIs in ASP.NET Core. They are designed for scenarios where you need a quick and minimalistic approach to expose endpoints without the overhead of a full-fledged MVC application. Why Minimal APIs? Efficiency: Write less, do more. A mantra for the modern developer. Performance: They’re lean, mean, and fast, perfect for high-performance scenarios. Ease of Use: New to .NET? No problem! Minimal APIs are accessible and easy to grasp. Flexibility: Simplicity doesn’t mean limited. From microservices to large-scale applications, they’ve got you covered. How Minimal APIs Work? Minimal APIs leverage the WebApplication class to define routes and handle HTTP requests. They rely on a functional approach, allowing developers to define endpoints using lambda expressions. Limitations of Minimal API No support for filters: For example, no support for IAsyncAuthorizationFilter, IAsyncActionFilter, IAsyncExceptionFilter, IAsyncResultFilter, and IAsyncResourceFilter. No support for model binding, i.e. IModelBinderProvider, IModelBinder. Support can be added with a custom binding shim. No support for binding from forms. This includes binding IFormFile. We plan to add support for IFormFile in the future. No built-in support for validation, i.e. IModelValidator No support for application parts or the application model. There's no way to apply or build your own conventions. No built-in view rendering support. We recommend using Razor Pages for rendering views. No support for JsonPatch No support for OData How to create a Minimal API? Creating a Minimal API closely mirrors the traditional approach, so you should encounter no significant challenges. It is a straightforward procedure that can be accomplished in just a few easy steps. Let's get started: Step 1: Open Visual Studio and select the ASP.NET Core Web API. Provide a preferred name for your project and select the location where you wish to store it. For the final step, choose the targeted framework, ensure that the "Configure for HTTPS" and "Enable OpenAPI support" checkboxes are checked, and, most importantly, leave the checkbox "Use controllers (uncheck to use Minimal API)" unchecked. Then, click the "Create" button. Step 2: Create one class with two fields and create one list class with some static values. namespace MinimalAPI { public class Student { public int Id { get; init; } public string Name { get; set; } } public static class StudentList { public static List<Student> student = new List<Student>() { new Student() { Id = 1, Name = "Test1", }, new Student() { Id = 2, Name = "Test2", }, new Student() { Id = 3, Name = "Test3", } }; } } Now add register new endpoint in Program.cs file. app.MapGet("GetAllStudent", () => StudentList.student); Run the project and see the output. I have added Create, Update and Delete student endpoint. See the full code below. using MinimalAPI; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // GetAll app.MapGet("GetAllStudent", () => StudentList.student); // GetById app.MapGet("GetByStudentId/{id}", (int id) => StudentList.student.FirstOrDefault(user => user.Id == id)); // Create app.MapPost("CreateStudent", (Student student) => StudentList.student.Add(student)); // Update app.MapPut("UpdateStudent/{id}", (int id, Student student) => { Student currentStudent = StudentList.student.FirstOrDefault(user => user.Id == id); currentStudent.Name = student.Name; }); // Delete app.MapDelete("DeleteStudent/{id}", (int id) => { var student = StudentList.student.FirstOrDefault(user => user.Id == id); StudentList.student.Remove(student!); }); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); Run the code and see the output. Conclusion: Minimal APIs in ASP.NET Core, introduced in .NET 6, offer a simplified and concise approach to building lightweight HTTP services, reducing boilerplate and emphasizing convention-based routing. While ideal for rapid development of small to medium-sized APIs, they lack advanced features found in traditional ASP.NET Core applications and may not be suitable for complex scenarios.
What is Authentication? Authentication is the process of validating the identity of a user or system attempting to access a protected resource. In C# programming, authentication is commonly implemented in various scenarios, including web applications, desktop applications, and services. Types of Authentications Basic Authentication Password-based Authentication Multi-Factor Authentication Token-based Authentication Let’s understand authentication with example. Here I am taking one example of MFA (Two-factor authentication). Step 1: Create the MVC Web Application Open Visual Studio and select File >> New >> Project. After selecting the project, a “New Project” dialog will open. Select ASP.NET Core web app (Model-View-Controller) and press Next and enter project name and click Next. Choose 'Individual Account' as the authentication type and click 'Create' to generate the project. Step 2: Adding QR Codes to configure two-factor authentication We will be using a QR code to configure and sync the Google authenticator app with our web app. Download the qrcode.js JavaScript library from https://davidshimjs.github.io/qrcodejs/ and put it into the “wwwroot\lib” folder in your application. Now, your “wwwroot” folder will have the following structure. Now, Add new scaffolded item in your project by right click on Area folder and select New scaffolded Item under Add section. Select Identity section on left sidebar and click on Add. Now, Select the identity files that you have to add to your project but select file “Account/Manage/EnableAuthenticator” is compulsory for 2FA. Select the DbContext Class of your project and click on add. Open the “Views\Manage\EnableAuthenticator.cshtml” file. You will find @section Scripts at the end of the file. Put the following code in it. @section Scripts { @await Html.PartialAsync("_ValidationScriptsPartial") <script src="~/lib/qrcode/qrcode.js"></script> <script type="text/javascript"> new QRCode(document.getElementById("qrCode"), { text: "@Html.Raw(Model.AuthenticatorUri)", width: 200, height: 200 }); </script> } Note: Change your script path as per your folder structure. This “EnableAuthenticator.cshtml” file already has a div with the id “qrCode” (see the code snippet below). We are generating a QR code inside that div using the qrcode.js library. We are also defining the dimensions of the QR code in terms of width and height. So finally, your “EnableAuthenticator.cshtml” file will look like this. @page @model EnableAuthenticatorModel @{ ViewData["Title"] = "Configure authenticator app"; ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; } <partial name="_StatusMessage" for="StatusMessage" /> <h3>@ViewData["Title"]</h3> <div> <p>To use an authenticator app go through the following steps:</p> <ol class="list"> <li> <p> Download a two-factor authenticator app like Microsoft Authenticator for <a href="https://go.microsoft.com/fwlink/?Linkid=825072">Android</a> and <a href="https://go.microsoft.com/fwlink/?Linkid=825073">iOS</a> or Google Authenticator for <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en">Android</a> and <a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8">iOS</a>. </p> </li> <li> <p>Scan the QR Code or enter this key <kbd>@Model.SharedKey</kbd> into your two factor authenticator app. Spaces and casing do not matter.</p> <div class="alert alert-info">Learn how to <a href="https://go.microsoft.com/fwlink/?Linkid=852423">enable QR code generation</a>.</div> <div id="qrCode"></div> <div id="qrCodeData" data-url="@Model.AuthenticatorUri"></div> </li> <li> <p> Once you have scanned the QR code or input the key above, your two factor authentication app will provide you with a unique code. Enter the code in the confirmation box below. </p> <div class="row"> <div class="col-md-6"> <form id="send-code" method="post"> <div class="form-floating mb-3"> <input asp-for="Input.Code" class="form-control" autocomplete="off" placeholder="Please enter the code."/> <label asp-for="Input.Code" class="control-label form-label">Verification Code</label> <span asp-validation-for="Input.Code" class="text-danger"></span> </div> <button type="submit" class="w-100 btn btn-lg btn-primary">Verify</button> <div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div> </form> </div> </div> </li> </ol> </div> @section Scripts { @await Html.PartialAsync("_ValidationScriptsPartial") <script src="~/lib/qrcode/qrcode.js"></script> <script type="text/javascript"> new QRCode(document.getElementById("qrCode"), { text: "@Html.Raw(Model.AuthenticatorUri)", width: 200, height: 200 }); </script> } When we execute the program, a QR code will be generated in this View. Then you can set up two factor authentication using the Google authenticator with the help of this QR code. Step 3: Configure two-factor authentication Before running the application, we need to apply migrations to our app. Navigate to Tools >> NuGet Package Manager >> Package Manager Console. It will open the Package Manager Console. Put in the “Update-Database” command and hit Enter. This will update the database using Entity Framework Code First Migrations. Run the application and click on “Register” in the top right corner of the homepage. You can see a user registration page. Fill in the details and click on the “Register” button as shown in the image below. Upon successful registration, you will be logged into the application and navigated to the home page. Here, you can see your registered Email id at the top right corner of the page. Click on it to navigate to the “Manage your account” page. Select “TwoFactorAuthentication” from the left menu. You will see a page similar to that shown below. Click on the “Set up authenticator app” button. You can see a QR code generated on your screen — it is asking for a “Verification Code”, also as shown in the image below. You need to install the Google Authenticator/Microsoft Authenticator app on your smartphone. It will allow you to scan this QR code in order to generate a Verification Code and complete a two-factor authentication setup. Open Microsoft Authenticator and click on verified IDs at the bottom. Click on “Scan a barcode” and scan the QR code generated by the web app. This will add a new account to Microsoft authenticator and generate a six-digit pin on your mobile screen. This is our two-factor authentication code. This is a TOTP ( time-based one-time password). You can observe that it keeps on changing frequently (life span of 30 seconds). Put this pin in the Verification Code textbox and click on verify. Upon successful verification, you will see a screen similar to the one shown below. This will give you the recovery codes for your account that will help to recover your account in case you are locked out. Take a note of these codes and keep them somewhere safe. Logout of the application and click on login again. Enter your registered email id and password and click on login. Now you can see a the two-factor authentication screen asking for the Authenticator code. Put in the code that is generated in your Google Authenticator app and click on Login. You will be successfully logged into the application and navigated to the home page.