[{"content":"MF Rural - The biggest brazilian rural e-commerce It started in early 2005, I was just 18 and I already was deeply immersed in IT work and rural fields.\nAt that time, I had already launched some professional websites to Canal do Boi (a rural national TV channel) and to some auction companies in Brazil.\nSo I received a visit from some guys from another state (São Paulo), who wanted to create a new way to make business in the rural field.\nIn fact, the concept already existed in other markets, such as eBay in the United States, but it wasn’t even imagined to use that for rural commerce. It sounded delusional to many people.. not to us.\nMF Rural helped connect buyers and sellers across a geographically dispersed agricultural market, reducing dependence on traditional in-person transactions and expanding commercial reach for rural businesses.\nOver the following years, the platform would grow to tens of thousands of users and become one of the largest rural marketplaces in Brazil.\nOur first stack was ASP+SQL Server and lots of HTML tables. ASP.NET 2.0 had just been released.\nI was at the center of all technical decisions and execution. Back in 2005, the industry simply called us \u0026ldquo;webmasters\u0026rdquo;.\nBut in practice, I was the sole developer, database administrator, and infrastructure engineer. There were no specialized DevOps or frontend teams.\nI built and sustained the entire architecture from the ground up. The website consisted of showing hundreds of rural products to users. The interactivity was innovative, with a short and safe sign-in form, users could buy directly from sellers, and under MF Rural monitor.\nThe market response exceeded our expectations. New user registrations grew from 787 in 2005 to 3,434 in 2006 and then to 13,446 in 2007, representing more than 17 times growth in just two years.\nTo put this into perspective, it’s a platform focused on rural brazilian market, and this market wasn’t known as a technological place, so that growth was really surprising, and showed us how our software was on the right track.\nIn those early days, our primary metric wasn\u0026rsquo;t conversion rate optimization. It was raw viability and survival.\nWe were bringing a traditional, offline market into the digital age during a time when broadband was scarce. The success of technical decisions, like migrating from LIKE queries to Full-Text Search, wasn\u0026rsquo;t measured in granular business KPIs, but in the fact that the database didn\u0026rsquo;t lock up during peak access, allowing the user base to keep growing without requiring massive, immediate hardware investments.\nBut, as we say in Brazil: life isn’t a strawberry. Or, to use a more familiar English expression, not everything was sunshine and rainbows.\nWith the rapid growth, new demands and challenges came, and soon we thought we had to migrate to a dedicated server, and I was responsible (again) to configure all needed services.\nThere wasn’t enough information on the internet. I had to deal with:\nRemote access Database services DNS Firewall configuration IIS Backups We faced two kinds of challenges:\nFirst, technically, we delivered a product to some hundreds of people at a time when broadband internet adoption was still limited . Second: make it easy for non technological people, with no UX information, or even studies. It wasn’t easy, but it was amazing to make me highly adaptable and gave me a deep, cross-functional understanding of web architecture. Created a great base to my evolution.\nAnd website evolution, the market demanded it, services were different from products, users demanded different ways to deal with technology… the work was constant, it wasn’t rare we work after 9 pm.\nHTTP wasn’t even enough.\nConfiguring SSL on IIS 6 felt like an adventure on its own.\nAt this time, security was becoming a real concern. I had to deal with SQL injection before it became a mainstream concern among web developers. Even then, I had taken precautions, data protection, daily backups, and parameterized queries.\nAnd as the platform grew, we started receiving SQL injection attempts, although the attacks exposed weaknesses common in web applications of that era, it reinforced the importance of secure coding practices. From that point forward, I strengthened input validation, query parameterization, and database access controls throughout the platform.\nIn parallel, I developed the MF Leilões embryo. Using ASP.NET, with auctions the challenge was real-time interaction. And .NET offered Project Atlas, an AJAX extension.\nI had used XMLHttpRequest, and Atlas was gold, considerably less lines to asynchronous communication. Using ScriptManager and UpdatePanel we could deliver the bid response to users.\nBy adopting asynchronous page updates through Project Atlas, we reduced the amount of data transferred during auction interactions and improved responsiveness for users participating in live bidding events.\nA few years later, it became part of .NET Framework 3.5. In the next posts I’ll talk more about it, because of the auction platform (turned into a new company, MF Leilões).\nScaling Search and Database Performance So, some years after, it already was time to evolve. In 2008 the ASP.NET version was launched. And new challenges came.\nOur team has grown too, we were two.\nSearching products using LIKE queries on the database didn’t really return what the user wanted. At this point, I need to study databases, mainly SQL Server, deeply.\nBigger amounts of data demanded best indexes, my first hands on with clustered and non-clustered, before that uniques had been enough. I redesigned the indexing strategy for the platform\u0026rsquo;s product catalog, improving query performance as the user base and listing volume continued to grow.\nAnd we started trying Full-Text Search. Response times decreased, and the results were more precise.\nSome users were so used to the old way, they knew how to search, to use key words. The solution was creating a mixed search engine, where users could decide how to search.\nIt helped a lot, made users happy. But as time passed, users saw how better the new way was, and the LIKE queries were forgotten. After the migration, searches that previously struggled with larger datasets became noticeably faster and more accurate.\nMoving to the Cloud Around 2010 I drove the team (we were 3 now) to start testing and using Cloud.\nInfrastructure management is increasingly complex. To gain flexibility and scalability we started using EC2, from AWS, one of our first experiences with cloud computing.\nObviously, nothing about “serverless” as we know nowadays. In many ways, it was like a usual dedicated server.\nBut you could, with 2 clicks, change all instance, it sounded like a dream. And it was useful.\nDifferent from many developers, infra guys, or devops, my approach was always to optimize software and database performance before increasing hardware resources. In many cases, query optimization, indexing strategies, and code improvements delivered better results than simply scaling infrastructure.\nBefore upgrading an EC2 instance, we first reviewed execution plans, indexing strategies, and application bottlenecks. But the consistent growth demanded more resources, and AWS provided us with it.\nBuilding a Video Processing Pipeline Then, another demand arose (one that would follow me throughout my career): handling video on the web. We needed to show videos in an era that there were many devices, each one with its own format or codec.\nAt that time, FFmpeg was the most practical solution available for handling video transcoding.\nToday this might seem straightforward, but at the time web video was fragmented across multiple formats, codecs, browsers, and devices. Even on MF Rural, nowadays we are using AWS Elemental MediaConvert.\nTo address this, I designed a transcoding workflow based on FFmpeg. An ASP.NET application orchestrated the conversion process, launching FFmpeg jobs and tracking their execution status.\nMain challenges:\nSecurity Job monitoring and execution tracking CPU exhaustion caused by video transcoding The solution proved successful. By 2015, more than 5,000 product listings included videos processed through the platform. The transcoding workflow remained in use for years and processed thousands of videos before being replaced by a managed cloud solution.\nBut I didn’t see that. Around 2013–2014, the company reached a stage where it required a full-time local team. I had others projects in parallel, and it wasn’t worth it.\nAnd I eventually left the project, but I’ve never lost contact, sometimes working as a consultant or a freelancer, and working directly with the brother-company, MF Leilões.\nComing Back Ten Years Later\u0026hellip; \u0026hellip;I received a call from the current MF Rural CEO, inviting me to contribute as a consultant on the next generation of the platform, now built with .NET 10 and Next.js.\nAfter evaluating the project\u0026rsquo;s scope and complexity, it became clear that a consulting role would not be enough. The platform had grown significantly, and many of the challenges required deep involvement in architecture, business rules, and technical decision-making.\nWe agreed that the best path forward was for me to rejoin the team full-time.\nBy then I was living roughly 700 kilometers away, so returning was not as simple as it had been in 2005. We eventually found a model that works well: three weeks working remotely and one week on-site each month. Given the company\u0026rsquo;s predominantly on-site operation, finding a workable balance between distance and collaboration was essential.\n","permalink":"https://potumati.com/posts/mf-rural-biggest-brazilian-rural-ecommerce/","summary":"\u003ch1 id=\"mf-rural---the-biggest-brazilian-rural-e-commerce\"\u003eMF Rural - The biggest brazilian rural e-commerce\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"MF Rural homepage\" loading=\"lazy\" src=\"/posts/mf-rural-biggest-brazilian-rural-ecommerce/mf-rural-home.png\"\u003e\u003c/p\u003e\n\u003cp\u003eIt started in early \u003cstrong\u003e2005\u003c/strong\u003e, I was just \u003cstrong\u003e18\u003c/strong\u003e and I already was deeply immersed in IT work and rural fields.\u003c/p\u003e\n\u003cp\u003eAt that time, I had already launched some professional websites to \u003cstrong\u003eCanal do Boi\u003c/strong\u003e (a rural national TV channel) and to some auction companies in Brazil.\u003c/p\u003e\n\u003cp\u003eSo I received a visit from some guys from another state (São Paulo), who wanted to create a new way to make business in the rural field.\u003c/p\u003e","title":"MF Rural: The Biggest Brazilian Rural E-commerce"},{"content":"Designing high-throughput background pipelines with System.Threading.Channels When our project receives thousands of requests per minute, we need to take some cares. Usually, the bottleneck isn\u0026rsquo;t CPU.\nProbabily the problem is one (or more) of these: I/O contention, thread starvation, uncontrolled concurrency, excessive allocations\u0026hellip; et cetera.\nOne of the most common mistakes in high-throughput (even in medium-throughput) APIs is coupling request processing with expensive secondary operations such as:\naudit logging; telemetry; webhook dispatching; email sending; analytics; event persistence. So it causes response times increasing, thread pressure growing and throughput collapses.\nThis is exactly the kind of problem System.Threading.Channels helps to deal with.\nWhat are Channels? They work on this concepts: producers, that produce (no way!) data and the consumers (surprise) consume, in an asynchronous way. And Channels coordinate flow control between both sides.\nTo make it easier to understand: it behaves like an in-memory asynchronous queue:\n1 [ Producers ] ---\u0026gt; ( Channel / Buffer ) ---\u0026gt; [ Consumers ] Different from traditional collections, Channels were designed around:\nasync-first APIs; low-allocation patterns; backpressure control; lock minimization; efficient coordination between concurrent workloads. Ok, we have ConcurrentQueue for that YES, but: ConcurrentQueue\u0026lt;T\u0026gt; solves thread safety.\nIt doesn\u0026rsquo;t solve asynchronous coordination.\nConsumers still need polling strategies, like timers, busy waiting, manual signaling, semaphores (we have a post on this blog about), custom synchronization logic.\nChannels solve both, concurrency safety and asynchronous coordination.\nThis removes unnecessary thread usage and significantly reduces contention under load.\nBackpressure matters (a lot) One of the most dangerous characteristics of internal pipelines is unbounded growth.\nIf producers generate work faster than consumers can process it, memory usage grows indefinitely.\nBounded channels allow configurable backpressure policies.\n1 2 3 4 5 6 7 8 9 10 using System.Threading.Channels; var options = new BoundedChannelOptions(1000) { FullMode = BoundedChannelFullMode.Wait, SingleReader = true, // This line makes just one consumer capable of read the channel SingleWriter = false }; var channel = Channel.CreateBounded\u0026lt;AuditEvent\u0026gt;(options); When the queue reaches capacity, you explicitly define what happens:\nWait: to hold the flow. DropNewest: to drop the new items. DropOldest: to drop the items that are waiting longer. DropWrite So, on this way, we know the behavior that is expected. (this post is getting bigger then I liked, but let\u0026rsquo;s keep going, the subject is worth it)\nWhy do Channels perform so well? Most Channel operations internally use ValueTask (a struct) instead of Task on synchronous operations.\nWhy does this matter?\nBecause asynchronous coordination happens constantly in high-throughput systems.\nAvoiding unnecessary heap allocations, helping at GC pressure.\nThis becomes especially important under sustained load.\nChannels are also heavily optimized for:\nlow-lock coordination; cache-friendly access patterns; asynchronous signaling; throughput-oriented workloads; A real-world scenario The last project I worked with, I needed to register all authenticated user actions/requests.\nIf I insert a SQL operation together the user interaction, it\u0026rsquo;d increase response latency dramatically.\nInstead:\nrequests enqueue lightweight events into a bounded channel. background workers process events asynchronously. batching strategies reduce I/O overhead (here background services play together) The API remains responsive while background throughput stays controlled.\nCould I use a fire-and-forget? Yes, I could. Should I? Absolutely not!\nWriting into the Channel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public interface IAuditEventQueue { ValueTask EnqueueAsync(AuditEvent auditEvent); } public class AuditEventQueue : IAuditEventQueue { private readonly Channel\u0026lt;AuditEvent\u0026gt; _channel; public AuditEventQueue() { var options = new BoundedChannelOptions(1000) { FullMode = BoundedChannelFullMode.Wait, SingleReader = true, SingleWriter = false }; _channel = Channel.CreateBounded\u0026lt;AuditEvent\u0026gt;(options); } public ValueTask EnqueueAsync(AuditEvent auditEvent) =\u0026gt; _channel.Writer.WriteAsync(auditEvent); public ChannelReader\u0026lt;AuditEvent\u0026gt; Reader =\u0026gt; _channel.Reader; } Inside the API:\n1 2 3 4 5 6 await _queue.EnqueueAsync(new AuditEvent { User = request.User.Id, RequestData = request.Data, CreatedAt = DateTime.UtcNow }); The request completes quickly without waiting for expensive database/log operations.\nBackgroundService combo! Channels become especially powerful when work with BackgroundService. I\u0026rsquo;d even say they are almost siamese twins. (ok, maybe I\u0026rsquo;m goint too far)\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class AuditEventWorker : BackgroundService { private readonly AuditEventQueue _queue; private readonly IAuditRepository _repository; public AuditEventWorker( AuditEventQueue queue, IAuditRepository repository) { _queue = queue; _repository = repository; } protected override async Task ExecuteAsync( CancellationToken stoppingToken) { while (await _queue.Reader.WaitToReadAsync(stoppingToken)) { var batch = new List\u0026lt;AuditEvent\u0026gt;(); while ( batch.Count \u0026lt; 50 \u0026amp;\u0026amp; _queue.Reader.TryRead(out var auditEvent)) { batch.Add(auditEvent); } if (batch.Count \u0026gt; 0) { await _repository.BulkInsertAsync(batch); } } } } Why is batching important Batching is one of the most important throughput optimizations in distributed systems.\nWith no batching, each database write is a round trip.\nIf you batch 50 or 100 events, you just \u0026lsquo;go to\u0026rsquo; the database server once. But be careful with oversized batches, because Channels are \u0026lsquo;in memory\u0026rsquo;, if the process crashes, you lost the data. Try to set a timer to deal with the batch, even it has fewer items you planned.\nIt reduces:\nnetwork round trips; transaction overhead; database lock contention. ReadAllAsync vs WaitToReadAsync For simple sequential processing, ReadAllAsync is usually enough.\n1 2 3 4 await foreach (var item in reader.ReadAllAsync(stoppingToken)) { await ProcessAsync(item); } However, when you need:\nbatching; buffering; throughput control; partial draining. WaitToReadAsync + TryRead provides much more flexibility.\nWhen aren\u0026rsquo;t Channels enough Channels are in-memory primitives.\nSo, if you wait a lot to deal with the messages (example: you configured a big batch), and the process crashes, you\u0026rsquo;ll lost your messages.\nAt the case I mentioned before (Audit), I have a 50 messages batch size\u0026hellip; BUT, each 10 seconds, the messages are processed, even they are fewer then 50.\nThis makes Channels ideal for:\ninternal pipelines; transient workloads; performance-sensitive coordination. But, when you need cross-service messaging, guaranteed delivery, et cetera, Channels aren\u0026rsquo;t your guy.\nFor those scenarios, technologies such as:\nKafka; RabbitMQ; Azure Service Bus; AWS SQS. are more appropriate. That\u0026rsquo;s it System.Threading.Channels is one of the most underrated (forgotten, in fact) concurrency primitives in modern .NET.\nIt provides:\nasync-first coordination; efficient backpressure handling; low allocation overhead; clean producer/consumer semantics. For many cases, they can eliminate the need for external queues entirely.\n","permalink":"https://potumati.com/posts/csharp-backpressure-aware-pipelines-in-net/","summary":"\u003ch1 id=\"designing-high-throughput-background-pipelines-with-systemthreadingchannels\"\u003eDesigning high-throughput background pipelines with System.Threading.Channels\u003c/h1\u003e\n\u003cp\u003eWhen our project receives thousands of requests per minute, we need to take some cares.\nUsually, the bottleneck isn\u0026rsquo;t CPU.\u003c/p\u003e\n\u003cp\u003eProbabily the problem is one (or more) of these: I/O contention, thread starvation, uncontrolled concurrency, excessive allocations\u0026hellip; et cetera.\u003c/p\u003e\n\u003cp\u003eOne of the most common mistakes in high-throughput (even in medium-throughput) APIs is coupling request processing with expensive secondary operations such as:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eaudit logging;\u003c/li\u003e\n\u003cli\u003etelemetry;\u003c/li\u003e\n\u003cli\u003ewebhook dispatching;\u003c/li\u003e\n\u003cli\u003eemail sending;\u003c/li\u003e\n\u003cli\u003eanalytics;\u003c/li\u003e\n\u003cli\u003eevent persistence.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eSo it causes response times increasing, thread pressure growing and throughput collapses.\u003c/p\u003e","title":"Designing High-Throughput Background Pipelines with System.Threading.Channels"},{"content":"Eduardo Potumati I am a Senior Software Engineer with over 20 years of experience in designing and delivering enterprise-grade systems across backend, frontend, and cloud platforms. My career has been defined by a commitment to technical excellence and the continuous evolution of the .NET ecosystem.\nTechnical Philosophy I approach software development as a craft that requires both rigor and pragmatism. My work is grounded in:\nArchitectural Integrity: I am a hands-on practitioner of Domain-Driven Design (DDD), Clean Architecture, and SOLID principles. I focus on building maintainable, scalable, and robust software solutions that solve complex business logic. Modernization: I have a proven track record of spearheading the migration of legacy .NET Framework applications to modern .NET (.NET 10), introducing new architectural standards and event-driven patterns. Scalability \u0026amp; Reliability: I specialize in building high-availability solutions, including real-time applications and large-scale portals capable of managing hundreds of concurrent visitors. Core Expertise Languages \u0026amp; Frameworks: C#, .NET Core, ASP.NET Web API, and MVC. Cloud Platforms: Extensive experience with Azure (Functions, Service Bus, App Service) and AWS (Lambda, EventBridge, S3, EC2). Data Systems: Proficient with SQL Server, PostgreSQL, MongoDB, and Entity Framework (EF Core). Integration: Solid background in SignalR, distributed system integration, and Event-Driven Architecture. Notable Impact Throughout my career, I have held the responsibility of ensuring uninterrupted 24/7 streaming services for major rural TV channels and maintaining online auction platforms that have facilitated over 1 million bids with sales exceeding R$350 million.\nI also developed and continue to maintain Adotar.com.br, Brazil\u0026rsquo;s largest pet rescue and adoption website, leveraging technology to support high-traffic social causes.\nProfessional Context I am a dual Italian and Brazilian citizen, eligible to work in the European Union. This blog serves as a space to share deep dives into .NET architecture, cloud patterns, and the technical challenges I encounter while building modern software.\nConnect with me:\nLinkedIn: linkedin.com/in/potumati GitHub: github.com/potumati ","permalink":"https://potumati.com/about/","summary":"\u003ch2 id=\"eduardo-potumati\"\u003eEduardo Potumati\u003c/h2\u003e\n\u003cp\u003eI am a \u003cstrong\u003eSenior Software Engineer\u003c/strong\u003e with over 20 years of experience in designing and delivering enterprise-grade systems across backend, frontend, and cloud platforms. My career has been defined by a commitment to technical excellence and the continuous evolution of the .NET ecosystem.\u003c/p\u003e\n\u003ch3 id=\"technical-philosophy\"\u003eTechnical Philosophy\u003c/h3\u003e\n\u003cp\u003eI approach software development as a craft that requires both rigor and pragmatism. My work is grounded in:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eArchitectural Integrity:\u003c/strong\u003e I am a hands-on practitioner of \u003cstrong\u003eDomain-Driven Design (DDD)\u003c/strong\u003e, \u003cstrong\u003eClean Architecture\u003c/strong\u003e, and \u003cstrong\u003eSOLID principles\u003c/strong\u003e. I focus on building maintainable, scalable, and robust software solutions that solve complex business logic.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eModernization:\u003c/strong\u003e I have a proven track record of spearheading the migration of legacy .NET Framework applications to modern \u003cstrong\u003e.NET (.NET 10)\u003c/strong\u003e, introducing new architectural standards and event-driven patterns.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eScalability \u0026amp; Reliability:\u003c/strong\u003e I specialize in building high-availability solutions, including real-time applications and large-scale portals capable of managing hundreds of concurrent visitors.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"core-expertise\"\u003eCore Expertise\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eLanguages \u0026amp; Frameworks:\u003c/strong\u003e C#, .NET Core, ASP.NET Web API, and MVC.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCloud Platforms:\u003c/strong\u003e Extensive experience with \u003cstrong\u003eAzure\u003c/strong\u003e (Functions, Service Bus, App Service) and \u003cstrong\u003eAWS\u003c/strong\u003e (Lambda, EventBridge, S3, EC2).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eData Systems:\u003c/strong\u003e Proficient with SQL Server, PostgreSQL, MongoDB, and Entity Framework (EF Core).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eIntegration:\u003c/strong\u003e Solid background in SignalR, distributed system integration, and Event-Driven Architecture.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"notable-impact\"\u003eNotable Impact\u003c/h3\u003e\n\u003cp\u003eThroughout my career, I have held the responsibility of ensuring uninterrupted \u003cstrong\u003e24/7 streaming services\u003c/strong\u003e for major rural TV channels and maintaining online auction platforms that have facilitated over \u003cstrong\u003e1 million bids\u003c/strong\u003e with sales exceeding \u003cstrong\u003eR$350 million\u003c/strong\u003e.\u003c/p\u003e","title":"About Me"},{"content":"C# Native AOT: Reaching Maximum Performance In the modern cloud landscape, where every millisecond and every megabyte counts toward your monthly bill, how we compile and deploy our code has become a strategic decision.\nAs developers, we are used to the standard .NET execution model: code is compiled into Intermediate Language (IL) and then compiled into machine code at runtime by the Just-In-Time (JIT) compiler. While the JIT is incredibly fast and optimized, modern architectures—especially Serverless and Edge Computing—demand something even more efficient.\nWhat is Native AOT? Native AOT (Ahead-of-Time) compilation changes the game by compiling C# code directly into native machine code during the build process. This removes the need for a JIT compiler at runtime.\nThe Strategic Benefits For a Senior Architect, the advantages of Native AOT translate directly into operational efficiency:\nFaster Startup (Cold Start): Since there is no JIT compilation phase at launch, applications start almost instantaneously. This is critical for Azure Functions or AWS Lambda, where latency on the first request can impact user experience. Reduced Memory Footprint: The application doesn\u0026rsquo;t need to load the JIT compiler or store IL data in memory, leading to significantly lower RAM usage. Smaller Binaries: Compared to self-contained JIT deployments, Native AOT produces smaller, leaner executables, which is ideal for containerized environments and Edge Computing. The \u0026ldquo;Senior\u0026rdquo; Reality Check: Trade-offs Engineering is about managing trade-offs. Native AOT is not a \u0026ldquo;silver bullet\u0026rdquo; for every project.\nReflection and Dynamic Code: Native AOT relies on static analysis. This means features like Reflection do not integrate well, as the compiler cannot always predict which code will be needed at runtime. If your architecture relies heavily on dynamic loading, AOT might not be for you. CI/CD Pipeline Impact: Compilation time is significantly longer compared to standard builds. As Tech Leads, we must account for this in our deployment automation strategies. Platform Specificity: Unlike IL, which is portable, an AOT-compiled binary is specific to the target OS and architecture (e.g., Linux x64). Conclusion Native AOT is a powerful tool for Microservices and Cloud-Native solutions where performance and resource density are paramount. By understanding when to trade the flexibility of the JIT for the raw speed of Native code, we can build more resilient and cost-effective systems.\nRuntime Performance BenchmarksA comparative analysis of memory footprint and startup latency across Standard Runtime, Trimmed Runtime, and Native AOT.\nThis article was originally inspired by my discussion on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-native-aot-performance/","summary":"\u003ch1 id=\"c-native-aot-reaching-maximum-performance\"\u003eC# Native AOT: Reaching Maximum Performance\u003c/h1\u003e\n\u003cp\u003eIn the modern cloud landscape, where every millisecond and every megabyte counts toward your monthly bill, how we compile and deploy our code has become a strategic decision.\u003c/p\u003e\n\u003cp\u003eAs developers, we are used to the standard .NET execution model: code is compiled into \u003cstrong\u003eIntermediate Language (IL)\u003c/strong\u003e and then compiled into machine code at runtime by the \u003cstrong\u003eJust-In-Time (JIT)\u003c/strong\u003e compiler. While the JIT is incredibly fast and optimized, modern architectures—especially \u003cstrong\u003eServerless\u003c/strong\u003e and \u003cstrong\u003eEdge Computing\u003c/strong\u003e—demand something even more efficient.\u003c/p\u003e","title":"C# Native AOT: High Performance for Cloud-Native Architectures"},{"content":"That classic question: List\u0026lt;T\u0026gt; or IEnumerable\u0026lt;T\u0026gt; in C#?\nStraight to the point:\nIEnumerable\u0026lt;T\u0026gt;: when you only need forward iteration. It also works with deferred execution, the basis of LINQ and ideal for handling large amounts of data without loading everything into memory. ICollection\u0026lt;T\u0026gt;: in addition to iteration, it allows you to add, remove, and count items, but without index access. IList\u0026lt;T\u0026gt;: includes everything from ICollection\u0026lt;T\u0026gt;, plus index-based access and the ability to insert/remove at specific positions. List\u0026lt;T\u0026gt;: the concrete implementation of IList\u0026lt;T\u0026gt;, the class you usually use to instantiate a list. Why does this matter? Whenever possible, use the minimal required interface. Clear contracts reduce coupling and make your code more flexible and easier to maintain.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-list-vs-ienumerable/","summary":"\u003cp\u003eThat classic question: \u003ccode\u003eList\u0026lt;T\u0026gt;\u003c/code\u003e or \u003ccode\u003eIEnumerable\u0026lt;T\u0026gt;\u003c/code\u003e in C#?\u003c/p\u003e\n\u003cp\u003eStraight to the point:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003eIEnumerable\u0026lt;T\u0026gt;\u003c/code\u003e\u003c/strong\u003e: when you only need forward iteration. It also works with deferred execution, the basis of LINQ and ideal for handling large amounts of data without loading everything into memory.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003eICollection\u0026lt;T\u0026gt;\u003c/code\u003e\u003c/strong\u003e: in addition to iteration, it allows you to add, remove, and count items, but without index access.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003eIList\u0026lt;T\u0026gt;\u003c/code\u003e\u003c/strong\u003e: includes everything from \u003ccode\u003eICollection\u0026lt;T\u0026gt;\u003c/code\u003e, plus index-based access and the ability to insert/remove at specific positions.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e\u003ccode\u003eList\u0026lt;T\u0026gt;\u003c/code\u003e\u003c/strong\u003e: the concrete implementation of \u003ccode\u003eIList\u0026lt;T\u0026gt;\u003c/code\u003e, the class you usually use to instantiate a list.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"why-does-this-matter\"\u003eWhy does this matter?\u003c/h3\u003e\n\u003cp\u003eWhenever possible, use the minimal required interface.\nClear contracts reduce coupling and make your code more flexible and easier to maintain.\u003c/p\u003e","title":"List\u003cT\u003e or IEnumerable\u003cT\u003e in C#: Which one to choose?"},{"content":"I recently published a detailed technical article on Balta.io—one of the premier .NET ecosystem references in Brazil—addressing a critical topic for high-performance systems: concurrency control.\nAs software engineers with extensive experience in high-demand solutions, we often face the challenge of \u0026ldquo;doing as much as possible, as fast as possible\u0026rdquo;. However, infrastructure realities—such as rate limits in third-party APIs or hardware constraints—force us to understand exactly when and how to throttle our execution.\nWhy read this article? In modern system development—particularly in Streaming scenarios and massive data processing where I have spent over 20 years of my career—the naive use of Task.WhenAll can lead to resource exhaustion or being blocked by external services.\nIn this guide, I explore:\nSemaphoreSlim: The ideal choice for modern applications, optimized for async/await with low memory overhead. Semaphore (Legacy/Global): When you truly need to step outside your application\u0026rsquo;s scope and control concurrency at the Operating System level (cross-process). Technical Highlight: The \u0026ldquo;Real-World\u0026rdquo; Scenario In the article, I use a practical analogy derived from common architectural challenges: controlling requests to a weather API and orchestrating heavy video transcoding processes.\nKey Takeaway: While SemaphoreSlim acts as an efficient traffic guard within your code, the classic Semaphore is capable of coordinating traffic between different \u0026ldquo;cities\u0026rdquo; (processes) on your server.\nRead the full article on Balta.io To check out the complete code examples, try/finally implementations with Release(), and the nuances between named and local semaphores, access the original link (in Portuguese):\n👉 Concurrency Control in C#: Semaphore and SemaphoreSlim - Balta.io\n","permalink":"https://potumati.com/posts/concurrency-control-semaphore-semaphoreslim/","summary":"\u003cp\u003eI recently published a detailed technical article on \u003cstrong\u003eBalta.io\u003c/strong\u003e—one of the premier .NET ecosystem references in Brazil—addressing a critical topic for high-performance systems: \u003cstrong\u003econcurrency control\u003c/strong\u003e.\u003c/p\u003e\n\u003cp\u003eAs software engineers with extensive experience in high-demand solutions, we often face the challenge of \u0026ldquo;doing as much as possible, as fast as possible\u0026rdquo;. However, infrastructure realities—such as rate limits in third-party APIs or hardware constraints—force us to understand exactly \u003cstrong\u003ewhen and how to throttle\u003c/strong\u003e our execution.\u003c/p\u003e","title":"Concurrency Control in C#: Semaphore and SemaphoreSlim"},{"content":"Too simple to be a complex type, too complex to be a simple attribute.\nThat\u0026rsquo;s what tuples in C# are.\nPerfect for returning multiple values from a method. No need to create a class (or even a record). Easy to read. Take a look at the image post and see how to use it.\nDo you use tuples, or do you prefer DTOs/records?\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-tuples-vs-dtos/","summary":"\u003cp\u003e\u003cstrong\u003eToo simple to be a complex type, too complex to be a simple attribute.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThat\u0026rsquo;s what tuples in C# are.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ePerfect for returning multiple values from a method.\u003c/li\u003e\n\u003cli\u003eNo need to create a class (or even a record).\u003c/li\u003e\n\u003cli\u003eEasy to read.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eTake a look at the image post and see how to use it.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDo you use tuples, or do you prefer DTOs/records?\u003c/strong\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e👉 \u003cem\u003eAdapted from my original post on \u003ca href=\"https://www.linkedin.com/posts/potumati_csharp-dotnet-programmingtips-activity-7371520354567135232-PENM?utm_source=social_share_send\u0026amp;utm_medium=member_desktop_web\u0026amp;rcm=ACoAAAGzxU4BM2J38YwZLdJjXXQDdoPvNE5m_d0\"\u003eLinkedIn\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e","title":"Tuples in C#: Too simple or too complex?"},{"content":"If you\u0026rsquo;re trying to insert millions of rows using INSERT in a loop, you\u0026rsquo;re doing it wrong.\nA few days ago, I was asked about it. It\u0026rsquo;s funny how a tool you use usually can slip our mind.\nAnyway, BULK INSERT is one of the most efficient ways to load large datasets into SQL Server. It can increase the insertion speed by up to 10x to 50x, which means that it can significantly reduce the time required for the operation.\nWhen used together with TABLOCK, the performance is even better, as SQL Server locks the entire table during the operation. This avoids transaction concurrency and the excess of logs, and avoids the need to manage numerous smaller locks.\n1 2 3 4 5 6 7 8 9 BULK INSERT dbo.Addresses FROM \u0026#39;C:\\data\\WorldAddresses.csv\u0026#39; WITH ( FIELDTERMINATOR = \u0026#39;,\u0026#39;, ROWTERMINATOR = \u0026#39;\\n\u0026#39;, FIRSTROW = 2, BATCHSIZE = 10000, TABLOCK ); Make sure the file encoding and row terminators match your configuration (\\r\\n vs \\n).\nKey parameters for BULK INSERT include: FIELDTERMINATOR: Defines the character that separates the columns in the text file. ROWTERMINATOR: Defines the character that separates the lines (usually \\n, representing the line break). BATCHSIZE: Defines how many records will be processed per batch. It impacts the performance and the amount of logs generated. FIRSTROW: Defines the first row to be processed. Why is it so fast? One of the main reasons is minimal logging.\nUnder the right conditions, SQL Server logs only allocation changes instead of every inserted row. This drastically reduces I/O and improves throughput.\nTo enable it:\nUse SIMPLE or BULK_LOGGED recovery model Use TABLOCK Prefer inserting into a heap or empty table DO NOT use when: You need to validate each line. There are triggers (complex) on the table. You need to insert data into multiple tables. The file is not in the same server as the database. You need logs of the operation. The table has many indexes (can severely degrade performance). You need transactional consistency across multiple operations. What if the data comes from my application? If the data is not stored in a file accessible by SQL Server, you can use SqlBulkCopy from .NET.\n1 2 3 4 5 6 7 using (var bulkCopy = new SqlBulkCopy(connection)) { bulkCopy.DestinationTableName = \u0026#34;dbo.Addresses\u0026#34;; bulkCopy.BatchSize = 10000; bulkCopy.BulkCopyTimeout = 0; bulkCopy.WriteToServer(dataTable); } BulkCopyTimeout = 0 is useful for very large loads to avoid premature timeouts.\nOf course, it\u0026rsquo;s slower than BULK INSERT because the data is sent over the network, but it\u0026rsquo;s still much faster than INSERT in a loop.\nTo be practical Before running BULK INSERT in production\nDisable non-essential indexes Disable triggers if possible Validate file encoding and delimiters Ensure enough disk space for the transaction log Test with a smaller batch first 👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/sql-server-bulk-insert/","summary":"\u003cp\u003e\u003cstrong\u003eIf you\u0026rsquo;re trying to insert millions of rows using \u003ccode\u003eINSERT\u003c/code\u003e in a loop, you\u0026rsquo;re doing it wrong.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eA few days ago, I was asked about it.\nIt\u0026rsquo;s funny how a tool you use usually can slip our mind.\u003c/p\u003e\n\u003cp\u003eAnyway, \u003ccode\u003eBULK INSERT\u003c/code\u003e is one of the most efficient ways to load large datasets into SQL Server. It can increase the insertion speed by up to 10x to 50x, which means that it can significantly reduce the time required for the operation.\u003c/p\u003e","title":"How to Insert Millions of Records into SQL Server"},{"content":"In C#, implicit operators enable automatic type conversions without the need for explicit casting. This functionality not only enhances code readability but also boosts development agility while safeguarding against runtime conversion exceptions.\nHowever, be careful.\nWhile implicit conversions offer convenience, excessive usage can potentially compromise code clarity. It\u0026rsquo;s essential to tread carefully as an overreliance on implicit conversions may obscure the precise moments and locations where conversions occur.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-implicit-operators/","summary":"\u003cp\u003eIn C#, \u003cstrong\u003eimplicit operators\u003c/strong\u003e enable automatic type conversions without the need for explicit casting. This functionality not only enhances code readability but also boosts development agility while safeguarding against runtime conversion exceptions.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHowever, be careful.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eWhile implicit conversions offer convenience, excessive usage can potentially compromise code clarity. It\u0026rsquo;s essential to tread carefully as an overreliance on implicit conversions may obscure the precise moments and locations where conversions occur.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e👉 \u003cem\u003eAdapted from my original post on \u003ca href=\"https://www.linkedin.com/posts/potumati_dotnet-csharp-cleancode-activity-7305241100657594369-hEi1?utm_source=social_share_send\u0026amp;utm_medium=member_desktop_web\u0026amp;rcm=ACoAAAGzxU4BM2J38YwZLdJjXXQDdoPvNE5m_d0\"\u003eLinkedIn\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e","title":"Implicit Operators in C#: Convenience vs. Clarity"},{"content":"Have you worked with Split Queries in EF Core before?\nWhen you incorporate related tables using EF Core, it typically generates a JOIN query, potentially leading to duplicated data. While this duplication is usually insignificant for small datasets, it can become problematic when dealing with larger volumes.\nWhen multiple collections are included, SQL JOINs multiply rows exponentially, not linearly.\n1 2 3 4 var posts = context.Posts .Include(p =\u0026gt; p.Comments) .Include(p =\u0026gt; p.Tags) .ToList(); Imagine a scenario where you have a table with 1000 posts and each post has 1000 comments. If you use the query below, you will get 1000 * 1000 = 1,000,000 rows in the result. This phenomenon is known as Cartesian Explosion, causing unexpected spikes in data loads.\n1 2 3 4 5 var posts = context.Posts .Include(p =\u0026gt; p.Comments) .Include(p =\u0026gt; p.Tags) .AsSplitQuery() .ToList(); The issue arises when the Posts table contains extensive columns like binary data or lengthy text fields. In such cases, the duplicated data is transmitted to the client multiple times, intensifying memory and network consumption.\nTo mitigate this, Split Queries come into play. EF Core employs separate queries instead of a single JOIN, effectively curbing data redundancy. Under the hood, EF Core executes one query per included collection and then performs the relationship fix-up in memory.\nHowever, this approach does introduce certain trade-offs:\nPros: Mitigates extensive duplicated data. Cons: Additional database round trips, more latency and, maybe, inconsistent data if the data changes between the queries. Exercise Caution! While Split Queries can enhance performance under specific circumstances, they might elevate the number of database round trips. Therefore, it\u0026rsquo;s advisable to implement them judiciously.\nWhen to use Split Queries When loading multiple collections (Include) When dealing with large payload columns (e.g. blobs, long text) When experiencing high memory usage or network overhead When NOT to use Small datasets Low-latency environments where round trips are expensive When strong consistency across the result set is required Split Queries are not a replacement for fixing N+1 problems - they solve a different class of performance issues.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/efcore-split-queries/","summary":"\u003cp\u003e\u003cstrong\u003eHave you worked with Split Queries in EF Core before?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eWhen you incorporate related tables using EF Core, it typically generates a \u003ccode\u003eJOIN\u003c/code\u003e query, potentially leading to duplicated data. While this duplication is usually insignificant for small datasets, it can become problematic when dealing with larger volumes.\u003c/p\u003e\n\u003cp\u003eWhen multiple collections are included, SQL JOINs multiply rows exponentially, not linearly.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv style=\"color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\n\u003ctable style=\"border-spacing:0;padding:0;margin:0;border:0;\"\u003e\u003ctr\u003e\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e1\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e2\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e3\n\u003c/span\u003e\u003cspan style=\"white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"\u003e4\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd style=\"vertical-align:top;padding:0;margin:0;border:0;;width:100%\"\u003e\n\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-csharp\" data-lang=\"csharp\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#8be9fd\"\u003evar\u003c/span\u003e posts = context.Posts\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .Include(p =\u0026gt; p.Comments)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .Include(p =\u0026gt; p.Tags)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    .ToList();\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eImagine a scenario where you have a table with 1000 posts and each post has 1000 comments. If you use the query below, you will get 1000 * 1000 = 1,000,000 rows in the result. This phenomenon is known as \u003cstrong\u003eCartesian Explosion\u003c/strong\u003e, causing unexpected spikes in data loads.\u003c/p\u003e","title":"Split Queries in EF Core: Mitigating Cartesian Explosion"},{"content":"Dependency Inversion (D) in SOLID: Why Is It So Important?\nThe Dependency Inversion Principle (D) reminds us that \u0026ldquo;high-level modules should not depend on low-level modules; both should depend on abstractions.\u0026rdquo; This means clients (code consuming functionalities) should rely on interfaces or abstractions, not concrete implementations.\nThis practice reduces coupling, simplifies changes, and makes code more testable.\nFor example, instead of directly coupling a notification service to a concrete class like EmailSender, use an interface such as IEmailSender. This allows you to easily swap implementations (e.g., SMTPClient, API service, message queue, etc) without modifying the client.\nThe result? A more flexible, modular system ready to grow over time.\nAre you applying this in your projects?\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/solid-dependency-inversion/","summary":"\u003cp\u003e\u003cstrong\u003eDependency Inversion (D) in SOLID: Why Is It So Important?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe Dependency Inversion Principle (D) reminds us that \u0026ldquo;high-level modules should not depend on low-level modules; both should depend on abstractions.\u0026rdquo; This means clients (code consuming functionalities) should rely on interfaces or abstractions, not concrete implementations.\u003c/p\u003e\n\u003cp\u003eThis practice reduces coupling, simplifies changes, and makes code more testable.\u003c/p\u003e\n\u003cp\u003eFor example, instead of directly coupling a notification service to a concrete class like \u003ccode\u003eEmailSender\u003c/code\u003e, use an interface such as \u003ccode\u003eIEmailSender\u003c/code\u003e. This allows you to easily swap implementations (e.g., \u003ccode\u003eSMTPClient\u003c/code\u003e, API service, message queue, etc) without modifying the client.\u003c/p\u003e","title":"Dependency Inversion (D) in SOLID: Why Is It So Important?"},{"content":"Convert a foreach loop to LINQ\nLINQ transforms a query into a first-class language construct in C#.\nIt can reduce the amount of code, enhance readability, and enable similar query expression patterns across different data sources.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-linq-vs-foreach/","summary":"\u003cp\u003e\u003cstrong\u003eConvert a \u003ccode\u003eforeach\u003c/code\u003e loop to LINQ\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eLINQ transforms a query into a first-class language construct in C#.\u003c/p\u003e\n\u003cp\u003eIt can reduce the amount of code, enhance readability, and enable similar query expression patterns across different data sources.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e👉 \u003cem\u003eAdapted from my original post on \u003ca href=\"https://www.linkedin.com/posts/potumati_csharp-dotnet-softwareengineering-activity-7247606346592751616-1zBE?utm_source=social_share_send\u0026amp;utm_medium=member_desktop_web\u0026amp;rcm=ACoAAAGzxU4BM2J38YwZLdJjXXQDdoPvNE5m_d0\"\u003eLinkedIn\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e","title":"Convert a foreach Loop to LINQ"},{"content":"Comparing Numbers with Tolerance in C#\nSometimes, when comparing two numbers in C#, you may want to allow for a tolerance or interval in the comparison.\nInstead of manually adding, subtracting, and comparing the values, you can use Math.Abs on the difference between the two numbers to check if the result falls within your acceptable range.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-comparing-numbers-math-abs/","summary":"\u003cp\u003e\u003cstrong\u003eComparing Numbers with Tolerance in C#\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eSometimes, when comparing two numbers in C#, you may want to allow for a tolerance or interval in the comparison.\u003c/p\u003e\n\u003cp\u003eInstead of manually adding, subtracting, and comparing the values, you can use \u003ccode\u003eMath.Abs\u003c/code\u003e on the difference between the two numbers to check if the result falls within your acceptable range.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e👉 \u003cem\u003eAdapted from my original post on \u003ca href=\"https://www.linkedin.com/posts/potumati_csharp-dotnet-softwareengineering-activity-7229822900357234689-lbT4?utm_source=social_share_send\u0026amp;utm_medium=member_desktop_web\u0026amp;rcm=ACoAAAGzxU4BM2J38YwZLdJjXXQDdoPvNE5m_d0\"\u003eLinkedIn\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e","title":"Comparing Numbers with Tolerance in C#"},{"content":"TempData - Transfer data between controllers\nIdeal for transferring small amounts of data between controllers in ASP.NET MVC.\nUnlike ViewData and ViewBag, which are specific to a single view, TempData persists for one request cycle.\nThis means the data remains available until it\u0026rsquo;s accessed, at which point it\u0026rsquo;s automatically removed.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-tempdata-vs-viewdata/","summary":"\u003cp\u003e\u003cstrong\u003eTempData - Transfer data between controllers\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIdeal for transferring small amounts of data between controllers in ASP.NET MVC.\u003c/p\u003e\n\u003cp\u003eUnlike \u003ccode\u003eViewData\u003c/code\u003e and \u003ccode\u003eViewBag\u003c/code\u003e, which are specific to a single view, \u003ccode\u003eTempData\u003c/code\u003e persists for one request cycle.\u003c/p\u003e\n\u003cp\u003eThis means the data remains available until it\u0026rsquo;s accessed, at which point it\u0026rsquo;s automatically removed.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e👉 \u003cem\u003eAdapted from my original post on \u003ca href=\"https://www.linkedin.com/posts/potumati_csharp-dotnet-softwareengineering-activity-7206626292559945729-OFrA?utm_source=social_share_send\u0026amp;utm_medium=member_desktop_web\u0026amp;rcm=ACoAAAGzxU4BM2J38YwZLdJjXXQDdoPvNE5m_d0\"\u003eLinkedIn\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e","title":"TempData in ASP.NET Core: Transferring Data Between Controllers"},{"content":"Concerned about duplicate URLs in ASP.NET MVC?\nTake care of both www and non-www domains. .NET already provides the correct method to handle them.\nUsing the UseRewriter middleware is the optimal approach.\nYou can use AddRedirectToNonWww() or AddRedirectToWww() for this purpose.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-duplicate-url-www-non-www/","summary":"\u003cp\u003e\u003cstrong\u003eConcerned about duplicate URLs in ASP.NET MVC?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eTake care of both \u003ccode\u003ewww\u003c/code\u003e and \u003ccode\u003enon-www\u003c/code\u003e domains. .NET already provides the correct method to handle them.\u003c/p\u003e\n\u003cp\u003eUsing the \u003ccode\u003eUseRewriter\u003c/code\u003e middleware is the optimal approach.\u003c/p\u003e\n\u003cp\u003eYou can use \u003ccode\u003eAddRedirectToNonWww()\u003c/code\u003e or \u003ccode\u003eAddRedirectToWww()\u003c/code\u003e for this purpose.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e👉 \u003cem\u003eAdapted from my original post on \u003ca href=\"https://www.linkedin.com/posts/potumati_csharp-dotnet-softwareengineering-activity-7196487003713028096-fv21?utm_source=social_share_send\u0026amp;utm_medium=member_desktop_web\u0026amp;rcm=ACoAAAGzxU4BM2J38YwZLdJjXXQDdoPvNE5m_d0\"\u003eLinkedIn\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e","title":"Handling Duplicate URLs in ASP.NET MVC: www vs non-www"},{"content":"Accessing Custom Attributes Using Reflection in C#\nCustom attributes provide a powerful way to attach declarative metadata to your code—whether it\u0026rsquo;s on classes, methods, properties, or fields. But how do you actually read that metadata at runtime?\nThis is where Reflection shines.\nBy using the System.Reflection, you can dynamically inspect your code\u0026rsquo;s structure and retrieve the information defined within those custom attributes. This functionality is the backbone for creating highly flexible applications, such as automated validation frameworks, custom JSON serializers, or dynamic API routing systems.\nWith just a few lines of Reflection code, your application can intelligently adapt its behavior based entirely on the metadata you\u0026rsquo;ve applied.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-access-attributes-reflection/","summary":"\u003cp\u003e\u003cstrong\u003eAccessing Custom Attributes Using Reflection in C#\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eCustom attributes provide a powerful way to attach declarative metadata to your code—whether it\u0026rsquo;s on classes, methods, properties, or fields. But how do you actually read that metadata at runtime?\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eThis is where Reflection shines.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eBy using the \u003ccode\u003eSystem.Reflection\u003c/code\u003e, you can dynamically inspect your code\u0026rsquo;s structure and retrieve the information defined within those custom attributes. This functionality is the backbone for creating highly flexible applications, such as automated validation frameworks, custom JSON serializers, or dynamic API routing systems.\u003c/p\u003e","title":"Accessing Custom Attributes Using Reflection in C#"},{"content":"Pattern Matching: Get Protected from Nulls\nC# pattern matching provides a substantially more concise syntax for testing expressions and taking decisive action when an expression matches.\nBy leveraging these structural features, you can gracefully bundle type checks, variable declarations, and null validations into a single, highly readable statement—significantly reducing boilerplate code and the risk of unexpected NullReferenceExceptions.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-pattern-matching-nulls/","summary":"\u003cp\u003e\u003cstrong\u003ePattern Matching: Get Protected from Nulls\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eC# pattern matching provides a substantially more concise syntax for testing expressions and taking decisive action when an expression matches.\u003c/p\u003e\n\u003cp\u003eBy leveraging these structural features, you can gracefully bundle type checks, variable declarations, and \u003ccode\u003enull\u003c/code\u003e validations into a single, highly readable statement—significantly reducing boilerplate code and the risk of unexpected \u003ccode\u003eNullReferenceException\u003c/code\u003es.\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e👉 \u003cem\u003eAdapted from my original post on \u003ca href=\"https://www.linkedin.com/posts/potumati_pattern-matching-get-protected-from-nulls-activity-7191043702696407042-LEUe?utm_source=social_share_send\u0026amp;utm_medium=member_desktop_web\u0026amp;rcm=ACoAAAGzxU4BM2J38YwZLdJjXXQDdoPvNE5m_d0\"\u003eLinkedIn\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e","title":"Pattern Matching in C#: Getting Protected from Nulls"},{"content":"Using Regular Expressions (Regex) is a highly effective method for segmenting your ASP.NET MVC routing.\nWith Regex route constraints, developers can create intricate matching patterns, leading to much more flexible and dynamic routing configurations directly in their controllers.\nImportant: Always exercise caution when using System.Text.RegularExpressions to process untrusted input, as poorly constructed patterns can be vulnerable to Regular Expression Denial of Service (ReDoS) attacks.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/aspnet-mvc-regex-routing/","summary":"\u003cp\u003e\u003cstrong\u003eUsing Regular Expressions (Regex) is a highly effective method for segmenting your ASP.NET MVC routing.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eWith Regex route constraints, developers can create intricate matching patterns, leading to much more flexible and dynamic routing configurations directly in their controllers.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eImportant:\u003c/strong\u003e Always exercise caution when using \u003ccode\u003eSystem.Text.RegularExpressions\u003c/code\u003e to process untrusted input, as poorly constructed patterns can be vulnerable to Regular Expression Denial of Service (ReDoS) attacks.\u003c/p\u003e\u003c/blockquote\u003e\n\u003chr\u003e\n\u003cp\u003e👉 \u003cem\u003eAdapted from my original post on \u003ca href=\"https://www.linkedin.com/posts/potumati_dotnet-csharp-aspnet-activity-7180915407166259201-pdg9?utm_source=social_share_send\u0026amp;utm_medium=member_desktop_web\u0026amp;rcm=ACoAAAGzxU4BM2J38YwZLdJjXXQDdoPvNE5m_d0\"\u003eLinkedIn\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e","title":"Using Regular Expressions for ASP.NET MVC Routing"},{"content":"Have you ever needed to handle accents in SQL?\nIf you are Latin, you have certainly done it many times.\nAnd how does it work in EF Core?\nIt\u0026rsquo;s easy, you just have to use EF.Functions.Collate!\nWarning: Overriding case-sensitivity in a query via EF.Functions.Collate (or by calling string.ToLower) can have a very significant impact on your application\u0026rsquo;s performance.\n🔗 Know more\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/efcore-handling-accents-sql/","summary":"\u003cp\u003e\u003cstrong\u003eHave you ever needed to handle accents in SQL?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIf you are Latin, you have certainly done it many times.\u003c/p\u003e\n\u003cp\u003eAnd how does it work in EF Core?\u003c/p\u003e\n\u003cp\u003eIt\u0026rsquo;s easy, you just have to use \u003ccode\u003eEF.Functions.Collate\u003c/code\u003e!\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eWarning:\u003c/strong\u003e Overriding case-sensitivity in a query via \u003ccode\u003eEF.Functions.Collate\u003c/code\u003e (or by calling \u003ccode\u003estring.ToLower\u003c/code\u003e) can have a very significant impact on your application\u0026rsquo;s performance.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003e🔗 \u003ca href=\"https://lnkd.in/dMs3HnVS\"\u003eKnow more\u003c/a\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e👉 \u003cem\u003eAdapted from my original post on \u003ca href=\"https://www.linkedin.com/posts/potumati_csharp-dotnet-netcore-activity-7178375191343874048-NJr8?utm_source=social_share_send\u0026amp;utm_medium=member_desktop_web\u0026amp;rcm=ACoAAAGzxU4BM2J38YwZLdJjXXQDdoPvNE5m_d0\"\u003eLinkedIn\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e","title":"Handling Accents in SQL with EF Core"},{"content":"Do you know the differences between Round, Floor, and Ceiling in C#?\nRound: It follows standard mathematical rounding to the nearest integer.\nFor example, 5.67 =\u0026gt; 6, and 5.47 =\u0026gt; 5.\nFloor: It rounds the number down to the nearest integer.\nFor example, 5.67 =\u0026gt; 5, and 5.47 =\u0026gt; 5.\nCeiling: It rounds the number up to the nearest integer.\nFor example, 5.67 =\u0026gt; 6, and 5.47 =\u0026gt; 6.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-math-round-floor-ceiling/","summary":"\u003cp\u003e\u003cstrong\u003eDo you know the differences between Round, Floor, and Ceiling in C#?\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eRound:\u003c/strong\u003e It follows standard mathematical rounding to the nearest integer.\u003cbr\u003e\nFor example, \u003ccode\u003e5.67 =\u0026gt; 6\u003c/code\u003e, and \u003ccode\u003e5.47 =\u0026gt; 5\u003c/code\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eFloor:\u003c/strong\u003e It rounds the number \u003cem\u003edown\u003c/em\u003e to the nearest integer.\u003cbr\u003e\nFor example, \u003ccode\u003e5.67 =\u0026gt; 5\u003c/code\u003e, and \u003ccode\u003e5.47 =\u0026gt; 5\u003c/code\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eCeiling:\u003c/strong\u003e It rounds the number \u003cem\u003eup\u003c/em\u003e to the nearest integer.\u003cbr\u003e\nFor example, \u003ccode\u003e5.67 =\u0026gt; 6\u003c/code\u003e, and \u003ccode\u003e5.47 =\u0026gt; 6\u003c/code\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cp\u003e👉 \u003cem\u003eAdapted from my original post on \u003ca href=\"https://www.linkedin.com/posts/potumati_csharp-netcore-developer-activity-7164321756680335360-ZYIj?utm_source=social_share_send\u0026amp;utm_medium=member_desktop_web\u0026amp;rcm=ACoAAAGzxU4BM2J38YwZLdJjXXQDdoPvNE5m_d0\"\u003eLinkedIn\u003c/a\u003e.\u003c/em\u003e\u003c/p\u003e","title":"Differences Between Round, Floor, and Ceiling in C#"},{"content":"Struct or class?\nMost types in a framework should be classes, but there are some situations in which the characteristics of a value type make it more appropriate to use structs.\nHere are some things to consider:\n👍 If your instances are small and short-lived, or commonly embedded in other objects, consider using a struct.\n👎 However, be cautious when defining a struct. Only use it if the type represents a single value, is immutable, has an instance size under 16 bytes, and will not need to be boxed frequently.\nKeep it in mind when defining new types in your code, and you\u0026rsquo;ll be able to make the best choice between a class or a struct.\n👉 Adapted from my original post on LinkedIn.\n","permalink":"https://potumati.com/posts/csharp-struct-vs-class/","summary":"\u003cp\u003e\u003cstrong\u003eStruct or class?\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eMost types in a framework should be classes, but there are some situations in which the characteristics of a value type make it more appropriate to use structs.\u003c/p\u003e\n\u003cp\u003eHere are some things to consider:\u003c/p\u003e\n\u003cp\u003e👍 If your instances are small and short-lived, or commonly embedded in other objects, consider using a \u003ccode\u003estruct\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e👎 However, be cautious when defining a \u003ccode\u003estruct\u003c/code\u003e. Only use it if the type represents a single value, is immutable, has an instance size under 16 bytes, and will not need to be boxed frequently.\u003c/p\u003e","title":"Struct or Class: When to Use Which in C#"}]