How the [Remote] Attribute Enhanced Our Registration Flow in ASP.NET Core
How the [Remote] Attribute Enhanced Our Registration Flow in ASP.NET Core In one of our production ASP.NET Core MVC projects, we faced a frustrating issue: users frequently submitted the registration form only to receive the message “Email already exists” after clicking Submit. While technically accurate, this was a poor user experience. To address this, we implemented the [Remote] attribute, leading to immediate improvements. The Scenario: Project type: SaaS web application Feature: User registration Problem: Email uniqueness was validated only after form submission. Users filled out lengthy forms, clicked Submit, and were rejected. This resulted in an increase in support tickets. We needed server-side accuracy while maintaining client-side speed. The Solution: [Remote] Attribute Validation. We introduced real-time server validation for the Email field. ViewModel: public class RegisterViewModel { [Required] [EmailAddress] [Remote(action: "CheckEmail", controller: "Account", ErrorMessage = "This email is already registered.")] public string Email { get; set; } } Controller: [HttpGet] public IActionResult CheckEmail(string email) { var exists = _userService.EmailExists(email); return Json(!exists); } As soon as the user leaves the Email field, an AJAX call checks the server and database, providing instant validation feedback without page reloads. Production Lessons Learned: Add Delay to Avoid Database Hammering - Remote validation triggers on focus-out. For high-traffic systems, consider debouncing on the client side. Always Re-Validate on Submit - While [Remote] enhances UX, it does not serve as a security layer. We still validate email uniqueness before saving. Explicit HTTP Method Matters - Missing [HttpGet] can lead to routing mismatches and random failures in staging. Missing Scripts = Broken Validation - We once deployed without: jquery.validate.unobtrusive.js Result? Validation worked locally, failed in production & Lesson learned. Impact After Release: Form submission errors dropped significantly Registration completion rate improved Support tickets reduced UX feedback improved instantly Small change. Big win. When We Avoid [Remote] Heavy business rules Multi-field dependencies Expensive DB joins In those cases, full submit validation is safer. Conclusion [Remote] is not flashy. But in real-world projects, it quietly removes friction where users feel it most. Used wisely, it’s one of the cleanest UX improvements in ASP.NET Core MVC.
Fixing Security issues raised by Veracode in .NET Core MVC
Fixing Security issues raised by Veracode in .NET Core MVC Static Analysis tools like Veracode don’t just point out problems they force us to write better, safer code. In .NET Core MVC projects, I often see the same security issues repeated, especially in fast-moving teams. Here are 3 real Veracode scenarios I’ve fixed recently and how you can fix them too: 1. SQL Injection (Even When You Think You’re Safe) Veracode Finding: Improper Neutralization of Special Elements used in an SQL Command Real Scenario: Developers build queries dynamically using string concatenation: var query = "SELECT * FROM Users WHERE Email = '" + email + "'";Even if input looks harmless, Veracode will flag it. Fix (Best Practice): Always use parameterized queries or LINQ (Entity Framework or other ORM): var user = _context.Users .FirstOrDefault(u => u.Email == email); Result: Secure + clean + Veracode -> Approved. 2. Cross-Site Scripting (XSS) in Razor Views Veracode Finding: Improper Neutralization of Script-Related HTML Tags Real Scenario: User input is directly rendered in Razor view: @Model.Comments If malicious script sneaks in, your UI becomes an attack surface. Fix: Let Razor auto-encode OR explicitly encode: @Html.Encode(Model.Comments) Or ensure data is sanitized before saving to DB. Defense in depth wins every time. 3. Insecure Cookie & Authentication Settings Veracode Finding: Sensitive Data Stored in Insecure Cookie Real Scenario: Authentication cookies not marked secure in production. Fix in Startup / Program.cs: services.ConfigureApplicationCookie(options => { options.Cookie.HttpOnly = true; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; options.Cookie.SameSite = SameSiteMode.Strict; }); This single fix often clears multiple Veracode findings at once. Whats the conclusion: Veracode isn’t “blocking your build” -> it’s training your codebase. -> Avoid string-based SQL, user ORM or paramterized queries. -> Trust Razor’s encoding (but verify) -> Lock down cookies & authentication -> Think like an attacker, code like a defender Security isn’t a phase. It’s a habit.
Are you confused in AddTransient() and AddScoped()? In .NET Core DI?
Its really easy to get confused between AddTransient() and AddScoped() in .NET Core dependency injection. This confusion is completely normal because, at a high level, both seem to look same but behave the differently. -> Let me explain this with simple example. Lets say, I am making an API request to get user details. Request flow will be like this: 1. A request to API endpoint -> /api/user/getuser/{slug} 2. The UserController & GetUser got called. 3. Inside the controller, two services are used: 3.1 UserProfileService 3.2 UserNotificationService 4. Both services depend on the same UserRepository to read the basic profile details and notification configuration from the database. -> Now let’s see what happens with different lifetimes. 1. AddTransient() 1. Consider if UserRepository is registered as AddTransient() lifetime in program.cs: 2. The request comes in 3. The UserController is created 4. UserProfileService is created → new UserRepository instance is created 5. Next, UserNotificationService is created → another new UserRepository instance is created. Conclusion - So within a single HTTP request, two separate repository objects are created. This is fine for small, stateless logic, but it can be inefficient for database access. 2. AddScoped() 1. Now consider if UserRepository is registered as AddScoped() lifetime in program.cs: 2. The request comes in 3. The UserController is created 4. UserProfileService is created → UserRepository instance is created 5. UserNotificationService is created → the same UserRepository instance is reused. Conclusion - So within one HTTP request, only one repository object exists, and both services share it. In real production systems, AddScoped() is usually the great choice for repositories, especially when working with databases or DbContext, because it keeps data consistent and avoids unnecessary object creation. I hope this simple flow-based explanation clears your confusion with dependency injection in .NET Core.
Abstraction is a powerful concept in software design
Abstraction is a powerful concept in software design, but over-abstraction can inadvertently harm your project. Here's a real experience from a .NET Core (C#) project. -> The Problem: Abstraction Done Too Early In one of our enterprise .NET Core applications, we aimed for “perfect architecture” and created multiple interfaces for a simple CRUD-based User module, including: - IUserService - IUserManager - IUserProcessor - IUserRepository - IBaseRepository<T> - IReadOnlyRepository<T> Example: public interface IUserService { Task<UserDto> GetUserAsync(int id); } public class UserService : IUserService { private readonly IUserManager _userManager; public UserService(IUserManager userManager) { _userManager = userManager; } public async Task<UserDto> GetUserAsync(int id) { return await _userManager.GetUserAsync(id); } } This resulted in: - No business logic - Just method-to-method forwarding - More files, more DI registrations, more confusion -> Real Impact on the Project - New developers took longer to understand the flow - Debugging required jumping across 4–5 layers - Change requests took more time - “Flexible architecture” became hard to maintain -> The Fix: Abstraction Where It Matters We refactored by asking, “Is this abstraction solving a real problem today?” What we kept: - Repository abstraction (DB may change) - Service layer only where business logic exists -> What we removed: - Pass-through interfaces - Premature layers -> Simplified version: public class UserService { private readonly UserRepository _repository; public UserService(UserRepository repository) { _repository = repository; } public async Task<UserDto> GetUserAsync(int id) { var user = await _repository.GetByIdAsync(id); if (!user.IsActive) throw new Exception("Inactive user"); return MapToDto(user); } } - Clear - Readable - Easy to change - Faster onboarding -> Following are the important points: - Abstraction is a tool, not a rule - Don’t abstract until you see variation or change - YAGNI still applies in modern .NET Core projects - Simple code scales better than “clever” architecture Good architecture evolves -> it is not forced on Day One.
.NET Core Tip: Boost Performance with Custom Gzip Compression Middleware
💡 .NET Core Tip: Boost Performance with Custom Gzip Compression Middleware Enhancing your application's performance is crucial, and one effective way to do this is by compressing HTTP responses using Gzip. Custom middleware in .NET Core makes it easy to implement Gzip compression, reducing the size of your responses and speeding up data transfer. Benefits: - Improved Performance: Faster load times for your users by reducing the amount of data transferred. - Reduced Bandwidth Usage: Lower data usage, which can be especially beneficial for mobile users. - Enhanced User Experience: Quicker response times lead to happier users and better engagement. Example: // Custom middleware to compress responses using Gzip public class GzipCompressionMiddleware { private readonly RequestDelegate _next; public GzipCompressionMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { var originalBodyStream = context.Response.Body; using (var compressedStream = new MemoryStream()) using (var gzipStream = new GZipStream(compressedStream, CompressionLevel.Fastest)) { context.Response.Body = gzipStream; await _next(context); context.Response.Body = originalBodyStream; compressedStream.Seek(0, SeekOrigin.Begin); await compressedStream.CopyToAsync(originalBodyStream); } } } // Register the middleware in the Startup class public void Configure(IApplicationBuilder app) { app.UseMiddleware<GzipCompressionMiddleware>(); // Other middleware registrations app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } By implementing custom Gzip compression middleware, you can significantly enhance your application's performance and provide a smoother experience for your users. Keep optimizing and happy coding! 🚀
C# Tip: The Power of the nameof Expression
🚀 C# Tip: The Power of the nameof Expression In C#, the nameof keyword is a simple yet powerful tool introduced in C# 6.0. It allows you to get the name of variables, types, methods, or members as a string, and the best part is, it works at compile time, not runtime. (csharp tip) 🔑 Key Benefits of nameof: - Avoid Magic Strings: The nameof expression helps you eliminate hardcoded strings that represent program elements. This ensures your code is less prone to errors and is much easier to maintain. For example, if you rename a variable or method, the compiler will catch mismatches instantly. - Enhanced Code Readability: Using nameof makes your code clearer. Instead of using arbitrary strings, it explicitly tells you which element you are referring to, improving the overall readability of your code. - Improved Performance: Since nameof is evaluated at compile time, there’s no additional runtime cost, making your code slightly faster when compared to using magic strings. 📚 Example: public class Employee { public string Name { get; set; } public void DisplayEmployeeInfo() { // Using nameof for method name Console.WriteLine($"Method: {nameof(DisplayEmployeeInfo)}"); // Using nameof for property name Console.WriteLine($"Property: {nameof(Name)}"); } } In this example, using nameof ensures that the property and method names are always in sync with the actual code, making the code more maintainable and less error-prone. 🔍 Why Use nameof in Your Code? - No more worrying about mismatched strings. - Easier refactoring when renaming methods or variables. - Clearer, more readable code. 💬 Have you used nameof in your projects? Feel free to share your thoughts or examples in the comments!
Why You Should Always Seal Your Classes
C# Tip: Why You Should Always Seal Your Classes Here’s a CSharp, tip I often share: Seal your classes by default. In C#, classes are inheritable unless explicitly marked as sealed. When a class is sealed, it means no other class can inherit from it. This is a simple but effective way to control your class design. I personally recommend sealing all classes unless you specifically need inheritance. Sealing your classes offers two main benefits: 1. Better Control: Sealing prevents any accidental or unwanted inheritance, making your code more predictable. 2. Improved Performance: When a class is sealed, the Just-In-Time (JIT) compiler can optimize your code better since it knows the class will never be extended. Example: public sealed class MyClass { public void DisplayMessage() { Console.WriteLine("Hello, world!"); } } In this example, MyClass is sealed, so it can't be inherited by any other class. By sealing your classes, you ensure better design and slight performance improvements. So, unless inheritance is necessary, always seal your classes. What do you think? Let me know in the comments below! 👇 If this tip was helpful, follow me for more daily C# insights!
Entity Framework Find and FirstOrDefault whats the difference
Entity Framework Short Tip! In Entity Framework, both Find and FirstOrDefault retrieve entities but differ in functionality: Find: 1. Looks up an entity by primary key. 2. First checks the local cache (context memory), then queries the database if not found. 3. Efficient for primary key lookups, avoiding unnecessary database calls. FirstOrDefault: 1. Retrieves the first entity matching a condition from the database. 2. Does not check the local cache, always queries the database. 3. Useful for complex queries or non-primary key lookups. Which is better? 1. Use Find for primary key lookups (better performance). 2. Use FirstOrDefault for more flexible, condition-based queries.
How to Effectively Block Spam in Contact Forms with .NET Core 8.0 MVC
How to Prevent Spam in Contact Forms with .NET Core 8.0 MVC – A Step-by-Step Guide Dealing with spam submissions in your lead or contact forms can be incredibly frustrating—especially when you’ve already implemented CAPTCHA and other spam prevention measures. But what if I told you there's a simple yet effective solution that could help you significantly reduce unwanted form submissions? In this post, I’ll walk you through a quick tip for blocking spam in your forms using .NET Core 8.0 MVC. While this solution is tailored to .NET Core, the logic can be adapted to other technologies as well, making it versatile and easy to implement across different platforms. Why Spam Forms Are a Problem Spammers often use automated bots or scripts to find and submit contact or lead forms on websites, flooding your inbox with irrelevant, sometimes harmful, content. CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) has been a popular solution for this, but it’s not foolproof. Bots are becoming smarter and can sometimes bypass CAPTCHA mechanisms. Luckily, there’s a much simpler method to keep spammers out while ensuring legitimate visitors can still submit forms without a hitch. The Simple Trick: Adding a Hidden Field The solution? A hidden input field. It’s a basic technique that prevents bots from submitting forms, as they typically “fill out” all fields, including hidden ones. By checking if this field is empty when the form is submitted, you can easily determine whether it was filled out by a bot or a human. Let’s take a look at how to implement this in a .NET Core 8.0 MVC application. Step 1: Build the Contact Form Here’s a basic contact or lead form that users can fill out. This example uses .NET Core MVC syntax: <form asp-action="contact" asp-controller="home" method="post"> <input class="form-control" type="text" maxlength="255" asp-for="FullName" placeholder="Your Name" required> <input class="form-control" type="email" maxlength="255" asp-for="Email" placeholder="Your Email" required> <input class="form-control" type="text" pattern="^[0-9]*$" maxlength="15" asp-for="Phone" placeholder="Your Phone with Country Code"> <textarea class="form-control" asp-for="Message" cols="40" rows="5" maxlength="1000" placeholder="Your Message" required></textarea> </form> Now, let’s add a hidden field to this form: <input class="additional-note" type="text" style="display:none;"> Step 2: Implement the Spam Check Logic When the form is submitted, we check whether the hidden field is filled out. If it’s empty, the submission is likely from a human. If it's not, it’s probably a bot, and we can discard the submission. In the controller, add the logic to handle this check: [HttpPost("contact")] [ValidateReCaptcha] public async Task<IActionResult> Contact(LeadModel model) { if (!ModelState.IsValid) return View(); try { bool result = await _contactService.SaveLead(model); TempData["success"] = result ? "We have received your request, and we'll get back to you shortly!" : "Sorry, we couldn't process your request."; return RedirectToAction("contact", "home"); } catch (Exception ex) { TempData["fail"] = "Sorry! Something went wrong while processing your request."; _logger.LogError(ex, $"Error occurred while saving lead - {Helper.Dump(model)}"); } return View(); } Step 3: Business Logic Service In the business logic service, we need to ensure that the lead is saved only if the hidden field is empty (indicating it wasn’t filled out by a bot): public async Task<bool> SaveLead(LeadModel? leadModel) { if (leadModel == null || !string.IsNullOrWhiteSpace(leadModel.RepeatLead)) return false; // Discard leads where the hidden field is filled out (likely spam). var lead = _mapper.Map<Lead>(leadModel); return await _contactRepository.SaveLead(lead); } How It Works: Bots vs Humans: Bots usually fill out all fields, including hidden ones, whereas humans won’t interact with hidden fields. Quick Spam Detection: If the hidden field is filled out, we treat the submission as spam and reject it. Seamless User Experience: Legitimate users can still submit the form as usual without any interruption. Why This Works Spammers use automated scripts to find and submit forms, but they don’t know about hidden fields that are intentionally left blank. This simple trick helps filter out spam without adding extra layers of complexity or affecting user experience. Plus, it’s incredibly easy to implement with .NET Core MVC. Conclusion: A Simple Yet Effective Spam Prevention Solution Implementing a hidden field in your forms is a quick and effective way to fight spam without over-complicating things. This approach works across various technologies, so feel free to adapt it to your tech stack. By using this method, you can keep your contact forms clean and only receive genuine submissions. 💪 What Do You Think? Have you tried similar techniques to block spam in your forms? What methods have worked best for you? Share your thoughts in the comments below! Also, feel free to share this post with anyone who might benefit from a spam-free experience on their website. Let’s keep our forms secure and user-friendly!
Understanding the Difference Between const and readonly in Csharp
🚀 C#/.NET Tip - Const vs Readonly 💡 💎 Understanding the Difference Between const and readonly in C# 🔹 Const: Constants are static by default. They must be assigned a value at compile-time. Can be declared within functions. Each assembly using them gets its own copy of the value. Can be used in attributes. 🔹 Readonly: Must be assigned a value by the time the constructor exits. Evaluated when the instance is created. Static readonly fields are evaluated when the class is first referenced. Example: public class MathConstants { public const double Pi = 3.14159; public readonly double GoldenRatio; public MathConstants() { GoldenRatio = (1 + Math.Sqrt(5)) / 2; } } Explanation: Pi is a const and its value is fixed at compile-time. It cannot be changed and is the same across all instances. 2. GoldenRatio is a readonly field, which is calculated at runtime when an instance of MathConstants is created. This allows for more flexibility as the value can be set in the constructor. This example highlights how const is used for values that are truly constant and known at compile-time, while readonly is used for values that are determined at runtime but should not change after being set. I hope this helps! 😊