Jenn Segal

Security Enthusiastic

14 post

125 followers

https://www.clouddefense.ai
About

Abhishek Arora, a co-founder and Chief Operating Officer at CloudDefense.AI, is a serial entrepreneur and investor. With a background in Computer Science, Agile Software Development, and Agile Product Development, Abhishek has been a driving force behind CloudDefense.AI’s mission to rapidly identify and mitigate critical risks in Applications and Infrastructure as Code.

Suggested for you
Sachin Tendulkar
@sachin_rt
Sachin Tendulkar
@sachin_rt
Sachin Tendulkar
@sachin_rt
Sachin Tendulkar
@sachin_rt
Show more
In the competitive landscape of software development, performance is a crucial factor that can make or break the success of your .NET applications. Whether you’re building enterprise-level web applications, APIs, or microservices, optimizing performance is essential for delivering a seamless user experience and maintaining competitiveness in the market. In this comprehensive guide, we’ll explore 15 best practices that can help you maximize the performance of your .NET applications, complete with detailed explanations and code examples. 1. Utilize Caching Caching is a fundamental technique for improving the performance of .NET applications by storing frequently accessed data in memory. This reduces the need to retrieve data from slower data sources such as databases. In .NET, you can leverage the MemoryCache class from the Microsoft.Extensions.Caching.Memory namespace. public class ProductController : ControllerBase { private readonly IMemoryCache _cache; private readonly IProductRepository _productRepository; public ProductController(IMemoryCache cache, IProductRepository productRepository) { _cache = cache; _productRepository = productRepository; } [HttpGet("{id}")] public IActionResult GetProduct(int id) { var cacheKey = $"Product_{id}"; if (!_cache.TryGetValue(cacheKey, out Product product)) { product = _productRepository.GetProduct(id); if (product != null) { // Cache for 10 minutes _cache.Set(cacheKey, product, TimeSpan.FromMinutes(10)); } } return Ok(product); } } In this example, the MemoryCache is used to store product data retrieved from the database. If the product is not found in the cache, it’s fetched from the database and then stored in the cache with an expiration time of 10 minutes. 2. Optimize Hot Code Paths Identify and optimize hot code paths, which are sections of code that are frequently executed and contribute significantly to the overall runtime of your application. By optimizing these paths, you can achieve noticeable performance improvements. public class OrderProcessor { public void ProcessOrder(Order order) { foreach (var item in order.Items) { // Optimize this section for performance } } } In this example, the ProcessOrder method represents a hot code path responsible for processing order details. It should be optimized for performance, possibly by using more efficient algorithms or data structures within the loop. 3. Use Asynchronous APIs Leverage asynchronous programming to handle multiple operations concurrently, improving scalability and responsiveness. Asynchronous APIs in .NET can be utilized using the async and await keywords. public async Task<IActionResult> GetDataAsync() { var data = await _dataService.GetDataAsync(); return Ok(data); } In this example, the GetDataAsync method asynchronously retrieves data from a service. This allows the application to continue executing other tasks while waiting for the data to be fetched. 4. Asynchronize Hot Code Paths Convert hot code paths to asynchronous operations to further enhance concurrency and responsiveness, especially in performance-critical sections of your code. public async Task ProcessOrdersAsync(List<Order> orders) { foreach (var order in orders) { // Asynchronously process each order await ProcessOrderAsync(order); } } private async Task ProcessOrderAsync(Order order) { // Asynchronously process order details await Task.Delay(100); // Example asynchronous operation } In performance-critical code paths, consider making asynchronous versions of methods to allow for non-blocking execution and improved concurrency. 5. Implement Pagination for Large Collections When dealing with large datasets, implement pagination to fetch data in smaller chunks, reducing memory consumption and improving performance. public IActionResult GetProducts(int page = 1, int pageSize = 10) { var products = _productRepository.GetProducts() .Skip((page - 1) * pageSize) .Take(pageSize) .ToList(); return Ok(products); } In this example, the GetProducts method retrieves a paginated list of products from the repository, allowing the client to request data in smaller chunks. 6. Prefer IAsyncEnumerable Use IAsyncEnumerable<T> for asynchronous enumeration of collections to prevent synchronous blocking and improve efficiency, especially with large datasets. public async IAsyncEnumerable<int> GetNumbersAsync() { for (int i = 0; i < 10; i++) { await Task.Delay(100); // Simulate asynchronous operation yield return i; } } 7. Cache Large Objects and Use ArrayPool Optimize memory usage by caching frequently used large objects and managing large arrays with ArrayPool<T>. public void ProcessLargeArray() { var largeArray = ArrayPool<int>.Shared.Rent(100000); // Process large array ArrayPool<int>.Shared.Return(largeArray); } 8. Optimize Data Access and I/O Minimize roundtrips and latency by optimizing data access and I/O operations, such as database queries and external API calls. public async Task<IActionResult> GetCachedDataAsync() { var cachedData = await _cache.GetOrCreateAsync("cached_data_key", async entry => { // Retrieve data from database or external service var data = await _dataService.GetDataAsync(); // Cache for 15 minutes entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(15); return data; }); return Ok(cachedData); } 9. Use HttpClientFactory Manage and pool HTTP connections efficiently with HttpClientFactory to improve performance when making HTTP requests. public async Task<IActionResult> GetRemoteDataAsync() { var client = _httpClientFactory.CreateClient(); var response = await client.GetAsync("https://api.example.com/data"); if (response.IsSuccessStatusCode) { var data = await response.Content.ReadAsStringAsync(); return Ok(data); } else { return StatusCode((int)response.StatusCode); } } 10. Profile and Optimize Middleware Components Profile and optimize frequently-called middleware components to minimize their impact on request processing time and overall application performance. // Example middleware component public class LoggingMiddleware { private readonly RequestDelegate _next; private readonly ILogger<LoggingMiddleware> _logger; public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger) { _next = next; _logger = logger; } public async Task Invoke(HttpContext context) { // Log request information _logger.LogInformation($"Request: {context.Request.Path}"); await _next(context); } } 11. Use Background Services for Long-Running Tasks Offload long-running tasks to background services to maintain application responsiveness and prevent blocking of the main application thread. public class EmailSenderService : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { // Check for pending emails and send them await SendPendingEmailsAsync(); // Wait for some time before checking again await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken); } } } 12. Compress Responses to Reduce Payload Sizes Enable response compression to reduce the size of HTTP responses, minimizing network bandwidth usage and improving overall application performance, especially for web applications. // Configure response compression in Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddResponseCompression(options => { options.EnableForHttps = true; options.MimeTypes = new[] { "text/plain", "text/html", "application/json" }; }); } 13. Stay Updated with Latest ASP.NET Core Releases Ensure your application is up-to-date with the latest ASP.NET Core releases to leverage performance improvements and new features provided by the framework. 14. Avoid Concurrent Access to HttpContext Avoid concurrent access to HttpContext as it is not thread-safe. Access to HttpContext should be synchronized to prevent race conditions and ensure application stability. // Example usage in controller action public IActionResult GetUserData() { var userId = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; // Retrieve user data using userId return Ok(userData); } 15. Handle HttpRequest.ContentLength Appropriately Handle scenarios where HttpRequest.ContentLength is null to ensure robustness and reliability in request processing, especially when dealing with incoming HTTP requests. // Example usage in controller action public async Task<IActionResult> ProcessFormDataAsync() { if (Request.ContentLength == null) { return BadRequest("Content length is not provided"); } // Process form data return Ok(); } By implementing these 15 best practices in your .NET applications, you can significantly enhance their performance, scalability, and user experience, enabling you to meet the demands of modern, high-performance software development. Remember, performance optimization is an ongoing process, so continuously monitor and refine your application to ensure it remains optimized for maximum efficiency.
9 min read   • Feb 5, 2025
In-memory caching stands as a cornerstone technique in optimizing the performance and scalability of ASP.NET Core applications. By storing frequently accessed data in memory, developers can drastically reduce the need to fetch information from slower data sources such as databases or external APIs. This leads to faster response times, improved resource utilization, and ultimately, a superior user experience. In this comprehensive guide, we’ll delve deep into the intricacies of in-memory caching, exploring its benefits, setup process, implementation strategies, and best practices. Understanding the Significance of In-Memory Caching: Accelerated Performance The primary advantage of in-memory caching lies in its ability to boost application performance. Retrieving data from memory is inherently faster than fetching it from disk or over the network. Consequently, in-memory caching significantly reduces latency, ensuring that users receive timely responses to their requests. Reduced Load on Data Sources By caching frequently accessed data, ASP.NET Core applications can alleviate the burden on external data sources, such as databases or web services. This reduction in load translates to improved scalability, as the application can handle more concurrent users without compromising performance. Enhanced User Experience In-memory caching contributes to an enhanced user experience by providing quick access to frequently requested information. Whether it’s product listings, user profiles, or dynamic content, caching ensures that data is readily available, resulting in a smoother and more responsive application interface. When to Use In-Memory Caching: While in-memory caching offers numerous benefits, it’s essential to use it judiciously and considerately. Here are some scenarios and considerations for leveraging in-memory caching effectively in ASP.NET Core applications: Frequently Accessed Data In-memory caching is most beneficial for data that is accessed frequently but changes infrequently. Examples include reference data, configuration settings, and static content. Caching such data in memory reduces the overhead of repeated database or file system accesses, leading to significant performance improvements. High-Volume Read Operations Applications with high-volume read operations, such as e-commerce platforms with product listings or content management systems with articles, can benefit greatly from in-memory caching. By caching frequently accessed data, these applications can handle large numbers of concurrent users while maintaining responsiveness and scalability. Expensive Computations or Queries In-memory caching can also be valuable for caching the results of expensive computations or database queries. By caching the computed or queried results, subsequent requests for the same data can be served quickly from memory, avoiding the need to repeat the costly operation. User Session Data For applications that store user-specific data during a session, such as shopping cart contents or user preferences, in-memory caching can provide a fast and efficient mechanism for managing session state. By caching session data in memory, the application can minimize latency and improve user experience. Temporary Data Storage In-memory caching can serve as a temporary data storage solution for transient data that doesn’t need to be persisted long-term. Examples include temporary authentication tokens, short-lived cache keys, or data used for the duration of a user session. Caching such data in memory can help reduce database load and improve application performance. Setting Up In-Memory Caching in ASP.NET Core: Configuring Services Enabling in-memory caching in an ASP.NET Core application begins with configuring the necessary services. This can be achieved in the Startup.cs file within the ConfigureServices method. public void ConfigureServices(IServiceCollection services) { services.AddMemoryCache(); // Add caching services // Additional service configurations... } By invoking the AddMemoryCache() method, developers incorporate the essential caching services into the application’s service container. Injecting IMemoryCache To utilize in-memory caching within various components of the application, developers need to inject the IMemoryCache interface. This is typically done through constructor injection in controllers, services, or other relevant classes. using Microsoft.Extensions.Caching.Memory; public class ProductController : ControllerBase { private readonly IMemoryCache _cache; public ProductController(IMemoryCache cache) { _cache = cache; } // Controller actions... } Implementing In-Memory Caching Strategies: With in-memory caching configured and IMemoryCache injected into the application components, developers can proceed to implement caching logic tailored to their specific use cases. Let’s explore a detailed example of caching product data in an ASP.NET Core web API. Example: Caching Product Data Suppose we have an endpoint in our web API for retrieving product details by ID. We want to cache the product data to improve performance and reduce database load. [HttpGet("{id}")] public async Task<IActionResult> GetProduct(int id) { // Attempt to retrieve the product from the cache if (_cache.TryGetValue($"Product_{id}", out Product cachedProduct)) { return Ok(cachedProduct); // Return the cached product } // If not found in cache, fetch the product from the data source (e.g., database or external API) Product product = await _productService.GetProductById(id); if (product != null) { // Cache the product with an expiration time of 5 minutes _cache.Set($"Product_{id}", product, TimeSpan.FromMinutes(5)); return Ok(product); // Return the fetched product } else { return NotFound(); // Product not found } } In this example: We attempt to retrieve the product with the given ID from the cache using TryGetValue(). If the product is found in the cache, it’s returned directly from memory. If not found, we fetch the product from the data source (e.g., database or external API) and cache it using Set() with an expiration time of 5 minutes. Syntax Examples: Here are examples of syntax for adding, removing, and other operations related to in-memory caching in ASP.NET Core using the IMemoryCache interface: Adding Data to Cache: _cache.Set("CacheKey", cachedData, TimeSpan.FromMinutes(10)); // Cache data with a key and expiration time Retrieving Data from Cache: if (_cache.TryGetValue("CacheKey", out CachedDataType cachedData)) { // Data found in cache, use cachedData } else { // Data not found in cache, fetch from source and cache it } Removing Data from Cache: _cache.Remove("CacheKey"); // Remove data from cache by key Clearing All Cached Data: _cache.Clear(); // Clear all data from cache Checking if Data Exists in Cache: if (_cache.TryGetValue("CacheKey", out CachedDataType cachedData)) { // Data exists in cache } else { // Data does not exist in cache } These syntax examples demonstrate how to perform common operations related to in-memory caching in ASP.NET Core using the IMemoryCache interface. From adding and retrieving data to removing entries and configuring cache options, developers can effectively utilize in-memory caching to optimize performance and enhance the user experience of their applications. Conclusion: In-memory caching represents a cornerstone technique for optimizing the performance and scalability of ASP.NET Core applications. By strategically caching frequently accessed data in memory, developers can minimize latency, reduce load on data sources, and deliver a superior user experience. Armed with a solid understanding of in-memory caching concepts and implementation strategies, developers can leverage this powerful tool to build high-performing and responsive applications that meet the demands of modern users.   Original Article - Mastering In-Memory Caching in ASP.NET Core
6 min read   • Jan 5, 2025
Introduction: Performance optimization and monitoring play a vital role in ensuring the efficiency, scalability, and reliability of ASP.NET Core applications. In this comprehensive guide, we’ll delve into various techniques, best practices, and tools for optimizing performance and monitoring the health of ASP.NET Core applications, accompanied by detailed examples for each concept. Optimizing Performance: 1. Efficient Database Querying with Entity Framework Core: Efficient database querying is crucial for improving application performance. With Entity Framework Core, we can optimize database access by minimizing the number of round-trips and optimizing query execution. Example: // Before Optimization var products = dbContext.Products.Where(p => p.CategoryId == categoryId).ToList(); // After Optimization var products = dbContext.Products.AsNoTracking().Where(p => p.CategoryId == categoryId).ToList(); In the optimized query, we use AsNoTracking() to disable change tracking for read-only operations, resulting in improved performance. 2. Caching Strategies for Improved Performance: Caching frequently accessed data can significantly reduce database round-trips and improve application response times. ASP.NET Core provides built-in caching mechanisms that we can leverage to implement caching strategies effectively. Example: public async Task<List<Product>> GetProductsAsync(int categoryId) { var cacheKey = $"products_{categoryId}"; if (!_cache.TryGetValue(cacheKey, out List<Product> products)) { products = await dbContext.Products.Where(p => p.CategoryId == categoryId).ToListAsync(); _cache.Set(cacheKey, products, TimeSpan.FromMinutes(10)); // Cache for 10 minutes } return products; } In this example, we cache the products retrieved based on the category ID for a specified duration, enhancing application performance. 3. Asynchronous Programming for Scalability: Asynchronous programming enables applications to handle multiple concurrent operations efficiently, leading to improved scalability and responsiveness. By utilizing async/await keywords in ASP.NET Core, we can execute asynchronous operations seamlessly. Example: public async Task<ActionResult> GetProductsAsync() { var products = await dbContext.Products.ToListAsync(); return Ok(products); } This example demonstrates retrieving products asynchronously from the database, enhancing the scalability of the application. 4. Compression for Network Efficiency: Compressing HTTP responses can significantly reduce bandwidth usage and improve network efficiency. ASP.NET Core supports response compression middleware, allowing us to compress HTTP responses before sending them to clients. Example: public void Configure(IApplicationBuilder app) { app.UseResponseCompression(); } By enabling response compression middleware, we can reduce data transfer sizes and improve overall network efficiency. 5. Lazy Loading for Improved Page Load Times: Lazy loading is a technique used to defer loading resources until they are needed, enhancing initial page load times and improving user experience. In ASP.NET Core applications, we can implement lazy loading for resources such as images and scripts. Example: <img src="placeholder.jpg" data-src="image.jpg" loading="lazy" alt="Lazy Loaded Image"> This example illustrates lazy loading of an image by specifying the actual image source in the data-src attribute, reducing initial page load times. Monitoring and Diagnostics: 1. Application Insights for Real-time Monitoring: Application Insights is a powerful tool provided by Azure for monitoring and diagnosing ASP.NET Core applications in real time. By integrating Application Insights into our application, we can gain valuable insights into application performance, availability, and usage. Example: public void ConfigureServices(IServiceCollection services) { services.AddApplicationInsightsTelemetry(Configuration["InstrumentationKey"]); } By adding Application Insights telemetry, we can monitor various metrics such as request rates, response times, and server resource utilization, enabling proactive performance monitoring and diagnostics. 2. Logging and Error Handling with Serilog: Logging is essential for capturing and analyzing application events, errors, and diagnostic information. Serilog is a popular logging library for ASP.NET Core applications, offering flexible configuration and various output sinks for logging data. Example: public void ConfigureLogger() { Log.Logger = new LoggerConfiguration() .WriteTo.File("log.txt") .CreateLogger(); } By configuring Serilog to log to a file, we can capture application events and errors for troubleshooting and analysis, enhancing the overall monitoring and diagnostics capabilities of the application. 3. Health Checks for Application Health Monitoring: Health checks are endpoints that provide information about the health of an application and its dependencies. By implementing health checks in ASP.NET Core applications, we can monitor the health and availability of critical components, such as databases, external services, and internal dependencies. Example: public void ConfigureServices(IServiceCollection services) { services.AddHealthChecks(); } This example demonstrates adding health checks to the ASP.NET Core application, enabling continuous monitoring of the application’s health and readiness. 4. Profiling and Performance Analysis with MiniProfiler: MiniProfiler is a lightweight profiling tool that helps identify performance bottlenecks in ASP.NET Core applications. By integrating MiniProfiler into our application, we can profile individual requests and identify areas for performance optimization. Example: public async Task<ActionResult> GetProductsAsync() { using (MiniProfiler.Current.Step("GetProductsAsync")) { var products = await dbContext.Products.ToListAsync(); return Ok(products); } } By wrapping the database query in a MiniProfiler step, we can measure the execution time and identify potential performance bottlenecks, enhancing the overall performance tuning and monitoring capabilities of the application. Conclusion: Optimizing performance and monitoring the health of ASP.NET Core applications are essential for delivering a seamless user experience and ensuring the reliability and scalability of the application. By implementing the techniques and best practices outlined in this guide, ASP.NET Core developers can optimize application performance, enhance scalability, and proactively monitor and diagnose issues, ensuring the smooth operation of their applications in production environments. Please find original article here.
5 min read   • Apr 22, 2024
Introduction LINQ (Language-Integrated Query) and Lambda expressions are powerful features in C# that allow developers to query and manipulate data in a concise and expressive manner. While both are used for similar purposes, they have distinct syntaxes and use cases. This document aims to provide a comprehensive comparison between LINQ and Lambda expressions in C#, along with code examples to illustrate their usage. LINQ (Language-Integrated Query): Definition: LINQ is a set of features in C# that enables developers to query data from different data sources using a SQL-like syntax directly within the C# language. Syntax: LINQ syntax consists of keywords such as from, where, select, group by, order by, etc., which resemble SQL syntax. Use Cases: LINQ is commonly used for querying data from collections (such as arrays, lists, and dictionaries), databases (using Entity Framework or LINQ to SQL), XML files, and other data sources. Code Example: Querying a list of objects using LINQ. List<int> numbers = new List<int> { 1, 2, 3, 4, 5 }; var evenNumbers = from num in numbers where num % 2 == 0 select num; Lambda Expressions: Definition: Lambda expressions are anonymous functions that allow developers to write inline delegate functions without explicitly defining a method. Syntax: Lambda expressions consist of the => (arrow) operator, parameters, and an expression or statement block. Use Cases: Lambda expressions are commonly used for sorting, filtering, and transforming data within LINQ queries, as well as in LINQ extension methods and delegates. Code Example: Filtering a list of objects using Lambda expressions. List<int> numbers = new List<int> { 1, 2, 3, 4, 5 }; var evenNumbers = numbers.Where(num => num % 2 == 0); Key Differences: Syntax: LINQ uses a declarative query syntax resembling SQL, while Lambda expressions use a more concise and functional syntax. Readability: LINQ queries are often more readable and intuitive for complex queries involving multiple operations, while Lambda expressions are preferred for simple and single-purpose operations. Flexibility: Lambda expressions provide more flexibility and can be used in various contexts beyond LINQ queries, such as delegates, event handlers, and LINQ extension methods. Let’s expand on the previous examples and provide multiple scenarios to illustrate the usage of LINQ and Lambda expressions in C#. 1. Filtering a List of Objects: Using LINQ: List<int> numbers = new List<int> { 1, 2, 3, 4, 5 }; var evenNumbers = from num in numbers where num % 2 == 0 select num; Using Lambda Expression: var evenNumbers = numbers.Where(num => num % 2 == 0); 2. Sorting a List of Objects: Using LINQ: List<string> fruits = new List<string> { "Apple", "Banana", "Orange", "Grape" }; var sortedFruits = from fruit in fruits orderby fruit descending select fruit; Using Lambda Expression: var sortedFruits = fruits.OrderByDescending(fruit => fruit); 3. Selecting Specific Properties from a List of Objects: Using LINQ: List<Person> people = new List<Person> { new Person { Name = "Alice", Age = 25 }, new Person { Name = "Bob", Age = 30 }, new Person { Name = "Charlie", Age = 28 } }; var names = from person in people select person.Name; Using Lambda Expression: var names = people.Select(person => person.Name); 4. Filtering and Projecting Data from a List of Objects: Using LINQ: List<Person> people = new List<Person> { new Person { Name = "Alice", Age = 25 }, new Person { Name = "Bob", Age = 30 }, new Person { Name = "Charlie", Age = 28 } }; var adults = from person in people where person.Age >= 18 select new { person.Name, person.Age }; Using Lambda Expression: var adults = people.Where(person => person.Age >= 18) .Select(person => new { person.Name, person.Age }); 5. Grouping Data from a List of Objects: Using LINQ: List<Person> people = new List<Person> { new Person { Name = "Alice", Age = 25 }, new Person { Name = "Bob", Age = 30 }, new Person { Name = "Charlie", Age = 28 } }; var ageGroups = from person in people group person by person.Age < 30 into youngGroup select new { IsYoung = youngGroup.Key, People = youngGroup.ToList() }; Using Lambda Expression: var ageGroups = people.GroupBy(person => person.Age < 30) .Select(youngGroup => new { IsYoung = youngGroup.Key, People = youngGroup.ToList() }); These examples demonstrate various scenarios where LINQ and Lambda expressions are used to query, filter, project, sort, and group data in C#. By understanding and mastering these features, developers can write concise and expressive code for data manipulation tasks. Note: The Person class used in these examples is assumed to have properties Name and Age. Conclusion: Both LINQ and Lambda expressions are essential features of C# that offer different approaches to querying and manipulating data. While LINQ provides a declarative and SQL-like syntax for querying data from various sources, Lambda expressions offer a more concise and functional approach for inline delegate functions. Understanding the differences between these two features will help developers choose the appropriate approach based on the specific requirements of their projects Please find original article here.
7 min read   • Apr 22, 2024
In the world of programming, understanding interfaces and abstract classes in C# is essential for creating flexible and maintainable code. In this blog post, we’ll explore these foundational concepts, discussing their definitions, differences, benefits, and providing clear examples in C#. Let’s dive into the basics of interfaces and abstract classes in C# programming! Interfaces: Definition: Interfaces in C# serve as contracts that define a set of methods or properties that implementing classes must adhere to. They declare the “what” of a class without specifying the “how,” promoting loose coupling and allowing for polymorphism. Interfaces are declared using the interface keyword and can be implemented by multiple classes. Syntax: Interfaces are declared using the interface keyword. interface IAnimal { void MakeSound(); } Example: Let’s consider a scenario where we have different animals in a zoo. We want each animal class to implement a method to make a sound. We can define an interface IAnimal: interface IAnimal { void MakeSound(); } Each animal class can then implement this interface and provide its own implementation of the MakeSound method: class Dog : IAnimal { public void MakeSound() { Console.WriteLine("Woof"); } } class Cat : IAnimal { public void MakeSound() { Console.WriteLine("Meow"); } } Benefits: Flexibility: Enables loose coupling between classes, promoting “programming to interfaces” rather than concrete implementations. Multiple Inheritance: Supports multiple inheritance, allowing a class to implement multiple interfaces. API Design: Defines contracts for classes, facilitating the design of flexible and interoperable APIs. Abstract Classes: Definition: Abstract classes in C# are classes that cannot be instantiated on their own and may contain abstract methods (methods without implementation) as well as concrete methods (methods with implementation). They provide a blueprint for derived classes to follow, enabling code reuse and providing a common base for related classes. Syntax: Abstract classes are declared using the abstract keyword. abstract class Shape { public abstract double GetArea(); } Example: Let’s consider a drawing application where we have different shapes, each with its own area calculation logic. We can define an abstract class Shape: abstract class Shape { public abstract double GetArea(); } Each shape class can inherit from this abstract class and provide its own implementation of the GetArea method: class Rectangle : Shape { public double Length { get; set; } public double Width { get; set; } public override double GetArea() { return Length * Width; } } class Circle : Shape { public double Radius { get; set; } public override double GetArea() { return Math.PI * Radius * Radius; } } Benefits: Code Reuse: Enables the definition of common behavior for related classes without implementing all methods. Default Implementation: Allows for the creation of methods with a default implementation. Initialization: Can have constructors, facilitating initialization of fields in subclasses. GitHub Differences and Considerations: Instantiation: Interfaces cannot be instantiated directly, while abstract classes cannot be instantiated unless all their abstract members are implemented by a concrete subclass. Multiple Inheritance: C# supports multiple inheritance through interfaces, but not through abstract classes (a class can only inherit from one abstract class). Method Implementation: In interfaces, all members are by default abstract and must be implemented by the implementing class. Abstract classes can have both abstract and concrete members, and the concrete members can be directly used by the subclass. Fields: Interfaces cannot contain fields, only members’ signatures. Abstract classes can contain fields, constructors, and members with implementation. Here’s a tabular format highlighting the differences between interfaces and abstract classes in C#: Feature Interfaces Abstract Classes Instantiation Cannot be instantiated directly Cannot be instantiated directly Inheritance Supports multiple inheritance Does not support multiple inheritance Members Can only contain method signatures (no fields or implementation) Can contain both abstract and concrete members (including fields and methods with implementation) Implementation All members are abstract (no implementation) Can have both abstract and concrete members Purpose Defines a contract for classes to implement Provides a blueprint for derived classes Example Syntax csharp interface IExample { void Method(); } csharp abstract class Example { public abstract void Method(); } Differences between interfaces and abstract classes This tabular format clearly outlines the key differences between interfaces and abstract classes in C#, making it easier to understand their distinctions. In conclusion, interfaces and abstract classes are powerful tools in C# for designing flexible and maintainable code. Understanding their differences and benefits is crucial for effective software design and development. Please find original article here.
5 min read   • Apr 22, 2024
In modern web applications, efficient data retrieval is crucial for providing a seamless user experience. Dynamic pagination with filters allows users to search and browse through large datasets effectively. In this guide, we’ll explore how to implement dynamic pagination with filters in an ASP.NET Core application step by step. In this guide, we’ll explore how to implement dynamic pagination with filters in an ASP.NET Core application step by step, enhancing usability and performance. Define Filter Model and Comparison Enum: Introduce the ExpressionFilter class and Comparison enum, which represent filter criteria and comparison operations, respectively. These components form the foundation for defining and applying filters in the ASP.NET Core application. Implement Expression Builder: Explore the ExpressionBuilder class, which provides methods for dynamically constructing LINQ expressions based on provided filters. The ConstructAndExpressionTree method generates an expression tree based on a list of filters, while the GetExpression method constructs a LINQ expression for a single filter criterion. Base Repository Interface and Implementation: Define the repository interface and implementation responsible for querying the database and applying filters for pagination. Discuss how filters are dynamically applied to the LINQ query, enabling efficient data retrieval based on user-defined criteria. Base Service Interface and Implementation: Explain the service interface and implementation for retrieving paginated data with filters. Highlight how the service interacts with the repository to fetch data and map it to view models, facilitating the creation of paginated data view models for presentation. Controller Setup: Detail the setup of the controller method to handle HTTP GET requests for retrieving paginated data with filters. Discuss how the controller accepts parameters for pagination, search criteria, and applies default values if not provided. Explore how filters are constructed based on the search criteria and applied to the ProductService to retrieve paginated data. 1. Define Filter Model and Comparison Enum public class ExpressionFilter { public string? PropertyName { get; set; } public object? Value { get; set; } public Comparison Comparison { get; set; } } public enum Comparison { [Display(Name = "==")] Equal, [Display(Name = "<")] LessThan, [Display(Name = "<=")] LessThanOrEqual, [Display(Name = ">")] GreaterThan, [Display(Name = ">=")] GreaterThanOrEqual, [Display(Name = "!=")] NotEqual, [Display(Name = "Contains")] Contains, //for strings [Display(Name = "StartsWith")] StartsWith, //for strings [Display(Name = "EndsWith")] EndsWith, //for strings }   The ExpressionFilter class represents a filter criterion with properties like PropertyName, Value, and Comparison. The Comparison enum enumerates various comparison operations like equal, less than, greater than, etc. 2. Implement Expression Builder public static class ExpressionBuilder { public static Expression<Func<T, bool>> ConstructAndExpressionTree<T>(List<ExpressionFilter> filters) { if (filters.Count == 0) return null; ParameterExpression param = Expression.Parameter(typeof(T), "t"); Expression exp = null; if (filters.Count == 1) { exp = GetExpression<T>(param, filters[0]); } else { exp = GetExpression<T>(param, filters[0]); for (int i = 1; i < filters.Count; i++) { exp = Expression.Or(exp, GetExpression<T>(param, filters[i])); } } return Expression.Lambda<Func<T, bool>>(exp, param); } public static Expression GetExpression<T>(ParameterExpression param, ExpressionFilter filter) { MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) }); MethodInfo startsWithMethod = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }); MethodInfo endsWithMethod = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }); MemberExpression member = Expression.Property(param, filter.PropertyName); ConstantExpression constant = Expression.Constant(filter.Value); switch (filter.Comparison) { case Comparison.Equal: return Expression.Equal(member, constant); case Comparison.GreaterThan: return Expression.GreaterThan(member, constant); case Comparison.GreaterThanOrEqual: return Expression.GreaterThanOrEqual(member, constant); case Comparison.LessThan: return Expression.LessThan(member, constant); case Comparison.LessThanOrEqual: return Expression.LessThanOrEqual(member, constant); case Comparison.NotEqual: return Expression.NotEqual(member, constant); case Comparison.Contains: return Expression.Call(member, containsMethod, constant); case Comparison.StartsWith: return Expression.Call(member, startsWithMethod, constant); case Comparison.EndsWith: return Expression.Call(member, endsWithMethod, constant); default: return null; } } } The ExpressionBuilder class provides methods for dynamically constructing LINQ expressions based on provided filters. The ConstructAndExpressionTree method constructs an expression tree based on a list of filters. The GetExpression method constructs a LINQ expression for a single filter criterion based on its comparison type. 3. Base Repository Interface and Implementation // Interface Task<PaginatedDataViewModel<T>> GetPaginatedDataWithFilter(int pageNumber, int pageSize, List<ExpressionFilter> filters, CancellationToken cancellationToken); // Implementation public async Task<PaginatedDataViewModel<T>> GetPaginatedDataWithFilter(int pageNumber, int pageSize, List<ExpressionFilter> filters, CancellationToken cancellationToken = default) { var query = _dbContext.Set<T>().AsNoTracking(); // Apply search criteria if provided if (filters != null && filters.Any()) { // Construct expression tree based on filters var expressionTree = ExpressionBuilder.ConstructAndExpressionTree<T>(filters); query = query.Where(expressionTree); } // Pagination var data = await query .Skip((pageNumber - 1) * pageSize) .Take(pageSize) .ToListAsync(cancellationToken); // Total count of data var totalCount = await query.CountAsync(cancellationToken); // Create and return paginated data view model return new PaginatedDataViewModel<T>(data, totalCount); } The repository interface defines a method GetPaginatedDataWithFilter to retrieve paginated data with filters. The repository implementation constructs a LINQ query dynamically based on the provided filters. Filters are applied to the query using the ExpressionBuilder class. Pagination is applied to the query to retrieve a specific subset of data. The total count of data is calculated. A paginated data view model containing the queried data and total count is created and returned. 4. Base Service Interface and Implementation // Interface Task<PaginatedDataViewModel<TViewModel>> GetPaginatedDataWithFilter(int pageNumber, int pageSize, List<ExpressionFilter> filters, CancellationToken cancellationToken); // Implementation public virtual async Task<PaginatedDataViewModel<TViewModel>> GetPaginatedDataWithFilter(int pageNumber, int pageSize, List<ExpressionFilter> filters, CancellationToken cancellationToken) { // Retrieve paginated data with filters from repository var paginatedData = await _repository.GetPaginatedDataWithFilter(pageNumber, pageSize, filters, cancellationToken); // Map data to view models var mappedData = _viewModelMapper.MapList(paginatedData.Data); // Create paginated data view model var paginatedDataViewModel = new PaginatedDataViewModel<TViewModel>(mappedData.ToList(), paginatedData.TotalCount); // Return paginated data view model return paginatedDataViewModel; } The service interface defines a method GetPaginatedDataWithFilter to retrieve paginated data with filters. The service implementation retrieves paginated data with filters from the repository. Retrieved data is mapped to view models using a view model mapper. A paginated data view model is created and returned. 5. Controller Setup [HttpGet("paginated-data-with-filter")] public async Task<IActionResult> Get(int? pageNumber, int? pageSize, string? search, CancellationToken cancellationToken) { try { // Setting default values for pagination int pageSizeValue = pageSize ?? 10; int pageNumberValue = pageNumber ?? 1; // List to hold filters var filters = new List<ExpressionFilter>(); // Check if search criteria is provided if (!string.IsNullOrWhiteSpace(search) && search != null) { // Add filters for relevant properties based on the search string filters.AddRange(new[] { new ExpressionFilter { PropertyName = "Code", Value = search, Comparison = Comparison.Contains }, new ExpressionFilter { PropertyName = "Name", Value = search, Comparison = Comparison.Contains }, new ExpressionFilter { PropertyName = "Description", Value = search, Comparison = Comparison.Contains } }); // Check if the search string represents a valid numeric value for the "Price" property if (double.TryParse(search, out double price)) { filters.Add(new ExpressionFilter { PropertyName = "Price", Value = price, Comparison = Comparison.Equal }); } } // Retrieve paginated data with filters from ProductService var products = await _productService.GetPaginatedDataWithFilter(pageNumberValue, pageSizeValue, filters, cancellationToken); // Create response containing paginated data var response = new ResponseViewModel<PaginatedDataViewModel<ProductViewModel>> { Success = true, Message = "Products retrieved successfully", Data = products }; // Return response return Ok(response); } catch (Exception ex) { // Log error _logger.LogError(ex, "An error occurred while retrieving products"); // Create error response var errorResponse = new ResponseViewModel<IEnumerable<ProductViewModel>> { Success = false, Message = "Error retrieving products", Error = new ErrorViewModel { Code = "ERROR_CODE", Message = ex.Message } }; // Return error response return StatusCode(StatusCodes.Status500InternalServerError, errorResponse); } } This controller method handles HTTP GET requests to retrieve paginated data with filters. It accepts parameters for pagination (pageNumber and pageSize) and a search string (search). Default values for pagination are set if not provided. Filters are constructed based on the search criteria, including properties like Code, Name, Description, and Price. The search string is checked to determine if it represents a valid numeric value for the Price property. Paginated data with filters is retrieved from the ProductService. A response containing paginated data is created and returned if successful. If an error occurs, it is logged, and an error response is returned. In conclusion, implementing dynamic pagination with filters in an ASP.NET Core application enhances the user experience by enabling efficient data retrieval and browsing capabilities. By following the steps outlined in this guide, developers can create web applications that provide seamless navigation through large datasets, improving usability and performance. Stay tuned for more in-depth tutorials and best practices in ASP.NET Core development! Please find original article here.
14 min read   • Apr 22, 2024
Introduction: In the world of modern web development, building robust and efficient APIs is essential for creating scalable and maintainable applications. ASP.NET Core provides a powerful framework for developing Web APIs, and in this blog post, we’ll explore a meticulously crafted ASP.NET Core Web API project template designed to deliver a secure, scalable, and elegant solution. Whether you’re a seasoned developer or just starting your journey with ASP.NET Core, this project template embodies best coding practices and architectural principles to help you build high-quality APIs with confidence. Project Overview: The ASP.NET Core Web API project template follows a clean architecture pattern, which promotes separation of concerns and modularity. It consists of three main directories: Core: Contains the core business logic, including domain models and services. Infrastructure: Handles infrastructure concerns such as data access and external services. API: Serves as the API layer, including controllers and extensions for handling HTTP requests and responses. Key Features: 1. Clean Architecture: The project structure is organized to promote a clear separation of concerns, making it easier to understand, test, and maintain the application. Here’s an example of the project structure: ├── src │ ├── Core │ ├── Infrastructure │ └── API ├── tests │ ├── Core.Tests │ ├── Infrastructure.Tests │ └── API.Tests └── README.md 2. SOLID Design Principles: The codebase follows SOLID principles, enhancing maintainability and extensibility. For example, the UserService class in the Core layer adheres to the Single Responsibility Principle (SRP) by encapsulating user-related business logic: // Core/Services/UserService.cs public class UserService { private readonly IUserRepository _userRepository; public UserService(IUserRepository userRepository) { _userRepository = userRepository; } public async Task<User> GetUserById(int userId) { // Logic to fetch user by Id from repository return await _userRepository.GetUserById(userId); } // Other methods... } 3. Repository Pattern: The repository pattern abstracts the data access layer and provides a consistent interface for working with data. Here’s an example of a repository class in the Infrastructure layer: // Infrastructure/Repositories/UserRepository.cs public class UserRepository : IUserRepository { private readonly DbContext _dbContext; public UserRepository(DbContext dbContext) { _dbContext = dbContext; } public async Task<User> GetUserById(int userId) { // Logic to fetch user by Id from database return await _dbContext.Users.FirstOrDefaultAsync(u => u.Id == userId); } // Other repository methods... } 4. Entity Framework Core: Entity Framework Core is used as the ORM tool for data access. The ApplicationDbContext class in the Infrastructure layer represents the database context: // Infrastructure/Data/ApplicationDbContext.cs public class ApplicationDbContext : DbContext { public DbSet<User> Users { get; set; } // Other DbSets... protected override void OnModelCreating(ModelBuilder modelBuilder) { // Configure entity mappings... } } 5. JWT for Token-based Authentication: Token-based authentication is implemented using JSON Web Tokens (JWT) for managing user sessions, authentication, and authorization securely. Here’s an example of an authentication controller in the API layer: // API/Controllers/AuthenticationController.cs public class AuthenticationController : ControllerBase { private readonly IAuthService _authService; public AuthenticationController(IAuthService authService) { _authService = authService; } [HttpPost("login")] public async Task<IActionResult> Login(LoginRequestModel model) { // Authenticate user and generate JWT token var token = await _authService.GenerateToken(model.Username, model.Password); return Ok(new { Token = token }); } // Other authentication methods... } 6. API Versioning: API versioning is embraced to support evolutionary changes while maintaining backward compatibility. Here’s an example of versioned controllers in the API layer: // API/Controllers/V1/UsersController.cs [ApiController] [ApiVersion("1.0")] [Route("api/v{version:apiVersion}/users")] public class UsersController : ControllerBase { // Controller methods for version 1.0 of the API... } // API/Controllers/V2/UsersController.cs [ApiController] [ApiVersion("2.0")] [Route("api/v{version:apiVersion}/users")] public class UsersController : ControllerBase { // Controller methods for version 2.0 of the API... } 7. Dependency Injection: ASP.NET Core’s built-in dependency injection container is used for managing and injecting dependencies throughout the application. Here’s an example of service registration in the Startup class: // API/Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddScoped<IUserService, UserService>(); services.AddScoped<IUserRepository, UserRepository>(); // Other service and repository registrations... } 8. Unit Testing: Separate test projects are included for unit testing the core, infrastructure, and API layers, ensuring code quality and reliability. Here’s an example of a unit test for the UserService class: Getting Started: https://github.com/kawser2133/web-api-project To use this project template, follow these steps: Ensure that you have the .NET 7 SDK installed on your machine. Clone or download the repository to your local machine. Open the solution in your preferred IDE (e.g., Visual Studio, Visual Studio Code). Build the solution to restore NuGet packages and compile the code. Configure the necessary database connection settings in the appsettings.json file of the Infrastructure project. Run the Update-Database command in the Package Manager Console to create the database. Run the application by starting the Project.API project. Conclusion: The ASP.NET Core Web API project template provides a solid foundation for building secure, scalable, and elegant APIs. By following best coding practices, architectural principles, and design patterns, developers can create robust APIs that meet the demands of modern web applications. Whether you’re building a small-scale project or a large enterprise application, this project template equips you with the tools and guidelines necessary for success.   Please find original article link here.
7 min read   • Apr 11, 2024
Introduction: Understanding Cookies and Sessions in Web Development – In web development, managing user data and state is crucial for creating personalized and secure experiences. Two common mechanisms for achieving this are cookies and sessions. In this article, we will explore what cookies and sessions are, their differences, and how they are used in web applications. Cookies: Cookies are small pieces of data stored on the user’s browser. They are sent between the client and the server with each HTTP request, allowing the server to recognize the user. Cookies are typically used for: Client-Side Storage: Cookies are stored on the client’s browser, making them an excellent choice for lightweight data that doesn’t require extensive server-side resources. Persistent Data: If you need to retain information between sessions and across different pages, cookies are suitable. They have expiration dates that can be set for short or extended periods. User Preferences: Cookies are commonly employed for storing user preferences, language settings, or themes, providing a personalized experience. How Cookies Work: When a user visits a website, the server sends a Set-Cookie header with the response. The cookie is then stored on the user’s browser. On subsequent requests, the cookie is sent back to the server using the Cookie header. Set-Cookie: user_id=123; expires=Wed, 01 Jan 2025 00:00:00 GMT; path=/ Here, a cookie named “user_id” is set with an expiration date and a path indicating its scope. Cookies in ASP.NET Core: ASP.NET Core provides a convenient way to work with cookies using the HttpContext.Response and HttpContext.Request objects. 1. Setting a Cookie: Cookies are small pieces of data sent by the server and stored on the client’s browser. Here’s how you can set a cookie in an ASP.NET Core controller action: // In a controller action public IActionResult SetCookie() { // Set a cookie with a key-value pair Response.Cookies.Append("user_id", "123", new CookieOptions { Expires = DateTime.Now.AddDays(7), // Cookie expiration date HttpOnly = true, // The cookie is not accessible via JavaScript Secure = true, // The cookie is only sent over HTTPS SameSite = SameSiteMode.Strict // Restricts the cookie to the same site }); return View(); } 2. Reading a Cookie: To read the value of a cookie, use the HttpContext.Request object in a controller action: public IActionResult ReadCookie() { // Retrieve the value of the "user_id" cookie string userId = Request.Cookies["user_id"]; // Use the retrieved value as needed return View(); } 3. Deleting a Cookie: Deleting a cookie involves setting its expiration date to a past date: public IActionResult DeleteCookie() { // Delete the "user_id" cookie by setting its expiration date to the past Response.Cookies.Delete("user_id"); return View(); } Sessions: Sessions are a way to store user-specific information on the server. Unlike cookies, which are stored on the client-side, sessions exist on the server and are identified by a unique session ID stored in a cookie on the user’s browser. Sessions are typically used for: Server-Side Storage: Sessions store data on the server, making them more secure for sensitive information. They are ideal for managing user authentication tokens and other confidential details. Temporary Data: If you only need to retain information for the duration of a user’s visit, sessions are well-suited. They automatically expire when the user closes the browser. Complex Data Structures: Sessions are preferable when dealing with larger sets of data or complex data structures, as the server handles the storage and retrieval. How Sessions Work: Session Creation: When a user visits a website, a unique session ID is generated, and a session object is created on the server. Session ID in Cookie: The session ID is sent to the user’s browser via a cookie. Server-Side Storage: The server uses the session ID to retrieve and store user-specific data in the session object. Session Expiry: Sessions usually have an expiration time, and when a user remains inactive for a specified period, the session is terminated. Sessions in ASP.NET Core: ASP.NET Core provides an abstraction for session management, and it can be configured to use various storage providers, such as in-memory storage, distributed cache, or a database. 1. Enabling Session: Sessions provide a way to store user-specific information on the server. In the Startup.cs file, configure services, and add session middleware: public void ConfigureServices(IServiceCollection services) { services.AddDistributedMemoryCache(); // In-memory cache for simplicity, use a more robust solution in production services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); // Session timeout options.Cookie.HttpOnly = true; // The session cookie is not accessible via JavaScript options.Cookie.IsEssential = true; // Mark the session cookie as essential }); // Other service configurations... } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseSession(); // Enable session middleware // Other middleware configurations... } 2. Storing and Retrieving Data in Session: Now, let’s see how to store and retrieve data in a session: public IActionResult SetSession() { HttpContext.Session.SetString("user_name", "John Doe"); HttpContext.Session.SetInt32("user_age", 25); return View(); } public IActionResult ReadSession() { string userName = HttpContext.Session.GetString("user_name"); int? userAge = HttpContext.Session.GetInt32("user_age"); // Use the retrieved values as needed return View(); } 3. Session Timeout: Adjust the session timeout to control how long session data is retained: services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); // Session timeout // ... }); 4. Session Essentiality: Mark a session cookie as essential to ensure its presence: services.AddSession(options => { // ... options.Cookie.IsEssential = true; // Mark the session cookie as essential }); Differences: Location: Cookies are stored on the client-side. Sessions are stored on the server-side. Data Storage: Cookies store small amounts of data (4KB per domain). Sessions can store larger amounts of data on the server. Security: Cookies can be manipulated or stolen, posing security risks. Sessions are more secure as session data is stored on the server. Conclusion: Cookies and sessions are fundamental components of web development, offering ways to manage user data and maintain state. The choice between cookies and sessions depends on the specific requirements of your application. While cookies are suitable for storing small, client-side data, sessions are preferable for managing larger amounts of server-side data. Understanding these mechanisms is essential for creating efficient and secure web applications. Please find original article here.
6 min read   • Mar 31, 2024
Understanding .NET Object Mapping: In the world of .NET development, working with objects and data transformation is a common task. To simplify this process, various object mapping libraries have emerged. In this guide, I’ll explore four popular .NET object mapping libraries: AutoMapper, TinyMapper, Mapster, and Inline Mapping. I’ll also provide examples and delve deeper into scenarios where each library shines.   Introduction to Object Mapping Object mapping, also known as object-to-object mapping, is the process of converting data from one object type to another. This is particularly useful when you need to transform data between different layers of your application or when integrating with external services or databases. Common Use Cases a. Database Mapping When working with databases, you often need to map the result of a database query to a .NET object or entity. Similarly, you might need to map .NET objects back to the database structure for data storage. b. API Integration When communicating with external APIs, you may receive data in JSON or XML format that needs to be mapped to .NET objects. Conversely, you might need to serialize .NET objects into the required format for API requests. c. ViewModel Creation In web applications, it’s common to map domain models to view models for rendering in views. This mapping ensures that sensitive or unnecessary data is not exposed to the client. Popular .NET Mappers 1. AutoMapper When to Use AutoMapper AutoMapper is a widely adopted and feature-rich object mapping library for .NET. It simplifies complex mappings and provides a fluent configuration API. Here are some scenarios where AutoMapper is an excellent choice: Complex Mappings: Use AutoMapper when dealing with complex mappings between objects with different structures. Configuration Flexibility: It offers extensive configuration options for fine-grained control over mappings. Large-Scale Projects: In large projects with many mappings, AutoMapper helps maintain a clear and organized mapping setup. Example // Configuration var config = new MapperConfiguration(cfg => { cfg.CreateMap<SourceClass, DestinationClass>(); }); // Mapping var mapper = config.CreateMapper(); var destination = mapper.Map<SourceClass, DestinationClass>(source); 2. TinyMapper When to Use TinyMapper TinyMapper is a lightweight and high-performance object mapping library for .NET. It focuses on simplicity and speed. Consider using TinyMapper in the following situations: Performance-Critical Applications: When performance is crucial, TinyMapper’s lightweight nature shines. Simple Mappings: For straightforward one-to-one property mappings without complex configurations. Quick Setup: TinyMapper is easy to set up and use, making it suitable for small to medium projects. Example // Mapping TinyMapper.Bind<SourceClass, DestinationClass>(); var destination = TinyMapper.Map<DestinationClass>(source); 3. Mapster When to Use Mapster Mapster is another lightweight and easy-to-use object mapping library for .NET. It emphasizes simplicity and performance. Here are scenarios where Mapster is a good fit: Simple to Moderate Mappings: When you need a balance between simplicity and flexibility. Performance-Oriented Applications: Mapster’s performance is suitable for applications with high data transformation requirements. Minimal Setup: Mapster requires minimal setup and configuration. Example // Mapping var destination = source.Adapt<DestinationClass>(); 4. Inline Mapping (Manual Mapping) When to Use Inline Mapping Inline mapping, also known as manual mapping, involves manually writing code to perform the mapping. While it doesn’t rely on an external library, it requires more manual effort. Use inline mapping in these scenarios: Full Control: When you want complete control over the mapping process and need to handle custom or complex transformations. Simple Mappings: For cases where using a library might be overkill and the mapping is straightforward. Small-Scale Projects: In smaller projects where the overhead of a mapping library isn’t justified. Example // Inline Mapping var destination = new DestinationClass { Property1 = source.Property1, Property2 = source.Property2 // ... }; Comparison and Choosing the Right Mapper Choosing the right object mapping library for your .NET project depends on your specific requirements. Here’s a summarized comparison: AutoMapper: Ideal for complex mappings and scenarios where you need a high degree of configuration control. It’s feature-rich but may be overkill for simple mappings. TinyMapper: Best for scenarios where performance is crucial due to its lightweight nature. It’s simpler to set up and use but has fewer features than AutoMapper. Mapster: Similar to TinyMapper, it’s lightweight and easy to use. Choose Mapster for straightforward mappings and when performance is essential. Inline Mapping: Suitable for simple mappings or when you want full control over the mapping process. It’s the most manual option but offers complete flexibility. Conclusion Choosing the right object mapping library for your .NET project depends on your specific needs. Consider factors like complexity, performance, and your familiarity with the library. Whether you opt for AutoMapper, TinyMapper, Mapster, or inline mapping, these tools will help streamline your data transformation tasks, making your code more maintainable and efficient.  
4 min read   • Mar 22, 2024
Understanding Numeric Data Types in C#: float, double, and decimal. When working with numeric data in C#, developers have several data types to choose from, each with its own characteristics and best-use scenarios. In this post, I’ll explore the differences between three common numeric data types: float, double, and decimal. Understanding these differences is crucial for making informed decisions when designing your C# applications. float: When Memory Efficiency Matters Single-precision floating-point Precision: About 7 significant decimal digits. Range: Represents a more limited range of values compared to double. Memory: Consumes 4 bytes (32 bits). Usage: Used when memory efficiency is critical, and the precision requirements are lower. Not suitable for financial or scientific applications requiring high precision. double: The Versatile Choice Double-precision floating-point Precision: About 15-17 significant decimal digits. Range: Represents a wide range of values, both large and small. Memory: Consumes 8 bytes (64 bits). Usage: Suitable for most general-purpose numerical calculations where very high precision is not required. It is not recommended for financial calculations due to potential rounding errors. decimal: Precision for Critical Applications Arbitrary-precision decimal Precision: About 28-29 significant decimal digits. Range: Suitable for representing a wide range of values with high precision. Memory: Consumes 16 bytes (128 bits), making it less memory-efficient than double or float. Usage: Recommended for financial calculations, currency representations, and applications where exact decimal precision is essential. It eliminates rounding errors associated with binary floating-point types (double and float). Choosing the Right Data Type Now that we’ve examined these data types, how do you choose the right one for your application? Precision Requirements: If you need to represent values with a high level of precision (e.g., financial calculations), decimal is the most appropriate choice due to its decimal-based precision. Memory Efficiency: If memory efficiency is crucial, especially when dealing with large datasets or arrays of numbers, float and double consume less memory than decimal. However, they sacrifice some precision. Performance: float and double operations are generally faster than decimal. If performance is a top priority and precision requirements can be met, consider using float or double. Domain-Specific Needs: Consider the requirements of your specific domain or application. Some industries, like finance or scientific computing, have standards that dictate the use of specific numeric types. In conclusion, the choice of a numeric data type in C# should align with your application’s precision, range, memory, and performance requirements. Use decimal for financial and monetary calculations where precision is critical, and choose double or float when precision can be traded for improved memory efficiency or performance. Understanding these distinctions empowers developers to make informed decisions, resulting in more efficient and accurate C# applications. Remember, the right choice of data type can make a significant difference in the success of your project.  
3 min read   • Mar 21, 2024