Blog

  • Entity Framework vs Entity Framework Core: What’s the Difference?

    Entity Framework vs Entity Framework Core: What’s the Difference?

    Many developers assume Entity Framework (EF6) and Entity Framework Core (EF Core) are simply different versions of the same framework.

    They’re not.

    EF Core was redesigned from the ground up to meet the needs of modern .NET applications. While both are Object-Relational Mappers (ORMs), they differ significantly in architecture, performance, and capabilities.

    Let’s explore the key differences.


    What is Entity Framework (EF6)?

    Entity Framework 6 is the classic ORM designed primarily for the .NET Framework.

    It simplifies database access by allowing developers to work with C# objects instead of writing SQL for every operation.

    EF6 is mature, stable, and still maintained, making it a suitable choice for many existing .NET Framework applications.


    What is Entity Framework Core?

    Entity Framework Core is Microsoft’s modern ORM for .NET (formerly .NET Core).

    It was rewritten with a focus on:

    • Cross-platform support
    • High performance
    • Better extensibility
    • Modern application development
    • Cloud-native workloads

    It powers most new ASP.NET Core applications today.


    Key Differences

    FeatureEntity Framework (EF6)Entity Framework Core
    Target Platform.NET FrameworkModern .NET (.NET 6/7/8/9+)
    Cross-Platform❌ No✅ Yes
    PerformanceGoodBetter for most workloads
    LINQ TranslationMatureMore optimized and actively evolving
    Dependency InjectionLimitedBuilt-in support
    Batch OperationsLimitedImproved support
    MigrationsSupportedMore flexible and actively developed
    Cloud & ContainersLimitedDesigned with modern deployment in mind

    Performance

    EF Core generally performs better because it was designed with performance in mind.

    It includes improvements such as:

    • Faster query execution
    • Better SQL generation
    • More efficient change tracking
    • Compiled queries
    • Split queries for complex object graphs

    These features can make a noticeable difference in high-traffic applications.


    Which One Should You Choose?

    Choose EF6 if:

    • You’re maintaining an existing .NET Framework application.
    • Migrating to modern .NET isn’t currently feasible.
    • The application is stable and doesn’t require new EF Core features.

    Choose EF Core if:

    • You’re starting a new project.
    • You’re building an ASP.NET Core application.
    • You need cross-platform support.
    • Performance and scalability are important.
    • You want access to the latest features and ongoing improvements.

    Migration Considerations

    Migrating from EF6 to EF Core isn’t always a simple package upgrade.

    Some APIs and behaviors differ, and applications with heavy customizations may require code changes and testing.

    Plan the migration carefully and validate functionality before moving to production.


    Final Thoughts

    Both EF6 and EF Core are capable ORMs, but they’re designed for different generations of .NET development.

    • Maintaining a legacy .NET Framework application? EF6 is still a solid choice.
    • Building a modern application? EF Core should be your default choice.

    Technology evolves, and EF Core represents Microsoft’s long-term direction for data access in the .NET ecosystem.

  • Repository Pattern vs Unit of Work in .NET: Do You Really Need Both?

    Repository Pattern vs Unit of Work in .NET: Do You Really Need Both?

    If you’ve worked on ASP.NET Core projects, you’ve probably seen classes like:

    • CustomerRepository
    • OrderRepository
    • UnitOfWork

    But with Entity Framework Core, an important question arises:

    Do we still need Repository and Unit of Work, or is DbContext enough?

    The answer depends on your application’s complexity and architectural goals.


    What Is the Repository Pattern?

    A Repository acts as an abstraction between your business logic and the data access layer.

    Instead of writing database queries throughout your application, all data operations are centralized.

    Example:

    public interface IProductRepository
    {
        Task<Product?> GetByIdAsync(int id);
        Task AddAsync(Product product);
        Task DeleteAsync(Product product);
    }
    

    The service interacts with the repository instead of directly with Entity Framework.

    Benefits

    • Separates business logic from data access.
    • Easier to mock during unit testing.
    • Centralizes reusable queries.
    • Improves maintainability in large projects.

    What Is the Unit of Work Pattern?

    The Unit of Work coordinates multiple repositories so they participate in a single transaction.

    Instead of saving changes after every repository operation, you commit them together.

    Example:

    await unitOfWork.Products.AddAsync(product);
    await unitOfWork.Orders.AddAsync(order);
    
    await unitOfWork.SaveChangesAsync();
    

    If one operation fails, the entire transaction can be rolled back.


    Doesn’t EF Core Already Provide These?

    Yes—and this is where many developers get confused.

    DbSet<T> behaves much like a Repository

    It already provides methods such as:

    • Add()
    • Remove()
    • Find()
    • Update()

    DbContext behaves like a Unit of Work

    It:

    • Tracks entity changes.
    • Coordinates updates.
    • Executes a single SaveChanges() or SaveChangesAsync().
    • Wraps changes in a transaction when appropriate.

    Because of this, many developers consider custom Repository and Unit of Work implementations unnecessary for simple CRUD applications.


    When Should You Use Custom Repository and Unit of Work?

    They make sense when:

    • Your application is large and follows Clean Architecture or Domain-Driven Design (DDD).
    • Multiple data sources need a consistent abstraction.
    • You have complex queries reused across many services.
    • You want to isolate EF Core from the application layer.
    • You anticipate changing the data access technology in the future.

    When Might They Be Unnecessary?

    For smaller applications that use EF Core exclusively, adding generic repositories can introduce extra complexity without much benefit.

    In these cases, injecting DbContext directly into services is often simpler and perfectly acceptable.


    My Rule of Thumb

    • Small to medium CRUD applications: DbContext is usually enough.
    • Large enterprise applications: A well-designed Repository and Unit of Work layer can improve organization, testability, and maintainability.

    The important point isn’t to follow a pattern blindly—it’s to choose the level of abstraction your project actually needs.


    Final Thoughts

    Repository and Unit of Work are valuable patterns, but they’re not mandatory in every ASP.NET Core application.

    EF Core already implements many of their responsibilities.

    Before adding another abstraction layer, ask yourself:

    Does this simplify my application, or does it simply add more code to maintain?

    Good architecture isn’t about using more patterns—it’s about using the right patterns for the problem you’re solving.

  • If-Else vs Ternary Operator in C#: When Should You Use Each?

    If-Else vs Ternary Operator in C#: When Should You Use Each?

    As C# developers, we often need to make decisions in our code.

    Two common ways to do this are:

    • if-else
    • The ternary operator (?:)

    Both achieve the same goal, but choosing the right one can make your code either easier—or harder—to read.

    Let’s explore when each approach is the better choice.

    Using if-else

    The if-else statement is ideal when the logic is more than a simple decision.

    Example:

    if (age >= 18)
    {
        category = "Adult";
    }
    else
    {
        category = "Minor";
    }
    

    This style is easy to understand, especially when additional conditions or multiple statements are involved.

    When to Use if-else

    Use if-else when:

    • You have multiple statements to execute.
    • The logic is complex.
    • You need nested conditions.
    • Readability is more important than brevity.

    Using the Ternary Operator

    The ternary operator provides a shorter way to assign a value based on a condition.

    Syntax:

    condition ? valueIfTrue : valueIfFalse;
    

    Example:

    string category = age >= 18 ? "Adult" : "Minor";
    

    This is concise, expressive, and easy to read.


    A Good Use Case

    The ternary operator works well for simple value assignments.

    string status = isActive ? "Active" : "Inactive";
    

    Simple.

    Readable.

    No unnecessary lines of code.


    A Bad Use Case

    Avoid chaining multiple ternary operators.

    string result = score >= 80
        ? "Excellent"
        : score >= 60
            ? "Good"
            : score >= 40
                ? "Average"
                : "Fail";
    

    Although valid, this quickly becomes difficult to read and maintain.

    An if-else block communicates the same logic much more clearly.


    Comparison

    ScenarioRecommended
    Simple value assignment✅ Ternary Operator
    Multiple statements✅ If-Else
    Complex business logic✅ If-Else
    Nested conditions✅ If-Else
    Short, readable conditions✅ Ternary Operator

    Best Practices

    ✔ Use the ternary operator only for simple expressions.

    ✔ Prefer if-else for complex decision-making.

    ✔ Avoid deeply nested ternary operators.

    ✔ Prioritize readability over writing fewer lines of code.

    Remember:

    Code is read far more often than it is written.


    Final Thoughts

    The ternary operator is a great tool—but like any tool, it should be used appropriately.

    If it makes your code clearer, use it.

    If it makes someone stop and think, an if-else statement is probably the better choice.

    The goal isn’t to write the shortest code—it’s to write code that’s easy for your future self and your teammates to understand.

  • Creating Custom Middleware Extensions in ASP.NET Core: Keep Your Program.cs Clean

    Creating Custom Middleware Extensions in ASP.NET Core: Keep Your Program.cs Clean

    As an ASP.NET Core application grows, Program.cs can quickly become cluttered with middleware registrations.

    For example:

    app.UseMiddleware<RequestLoggingMiddleware>();
    app.UseMiddleware<ApiKeyMiddleware>();
    app.UseMiddleware<TenantMiddleware>();
    app.UseMiddleware<RequestValidationMiddleware>();
    app.UseMiddleware<MaintenanceModeMiddleware>();
    

    While this works, it’s not the cleanest approach.

    A better practice is to create extension methods that encapsulate middleware registration.


    Step 1: Create Your Middleware

    public class RequestLoggingMiddleware
    {
        private readonly RequestDelegate _next;
    
        public RequestLoggingMiddleware(RequestDelegate next)
        {
            _next = next;
        }
    
        public async Task InvokeAsync(HttpContext context)
        {
            Console.WriteLine($"Request: {context.Request.Path}");
    
            await _next(context);
    
            Console.WriteLine($"Response: {context.Response.StatusCode}");
        }
    }
    

    Step 2: Create an Extension Method

    Create a static extension class:

    public static class RequestLoggingMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestLogging(
            this IApplicationBuilder app)
        {
            return app.UseMiddleware<RequestLoggingMiddleware>();
        }
    }
    

    Now your middleware has a meaningful and reusable registration method.


    Step 3: Register It

    Instead of:

    app.UseMiddleware<RequestLoggingMiddleware>();
    

    Use:

    app.UseRequestLogging();
    

    This makes Program.cs much cleaner and easier to read.


    Why Use Extension Methods?

    ✅ Improved Readability

    Instead of exposing implementation details, your pipeline reads like documentation.

    app.UseRequestLogging();
    app.UseApiKeyValidation();
    app.UseTenantValidation();
    app.UseMaintenanceMode();
    

    ✅ Better Maintainability

    If the middleware implementation changes, the registration code remains the same.


    ✅ Reusability

    The extension method can be shared across multiple projects or packaged into a reusable library.


    ✅ Encapsulation

    Consumers don’t need to know whether you’re using:

    • UseMiddleware<T>()
    • Multiple middleware components
    • Additional configuration

    Everything is hidden behind a simple method.


    Real-World Example

    Many built-in ASP.NET Core middleware components follow this pattern:

    app.UseAuthentication();
    
    app.UseAuthorization();
    
    app.UseCors();
    
    app.UseSession();
    
    app.UseSwagger();
    

    These are all extension methods internally.

    Following the same pattern makes your custom middleware feel like a natural part of ASP.NET Core.


    Best Practices

    ✔ Keep each middleware focused on a single responsibility.

    ✔ Give extension methods meaningful names.

    ✔ Place extensions in a dedicated Extensions folder.

    ✔ Use XML comments if the middleware is part of a shared library.


    Final Thoughts

    Creating extension methods for custom middleware is a small change that greatly improves the readability and maintainability of your application.

    A clean Program.cs makes it easier for new developers to understand the request pipeline and keeps your startup configuration organized.

    When building professional ASP.NET Core applications, aim for code that is not only functional but also easy to read and maintain.

  • What If a User Uploads a Dangerous File to Your Server? Here’s How to Prevent It.

    What If a User Uploads a Dangerous File to Your Server? Here’s How to Prevent It.

    File upload is one of the most common features in web applications—but it’s also one of the most targeted by attackers.

    Imagine a user uploads a file named:

    invoice.pdf.exe
    

    Or renames a malicious executable to:

    resume.pdf
    

    If your application trusts only the file name or extension, you could expose your server to serious security risks.

    A secure file upload system should never rely on a single validation step.

    1. Validate the File Extension

    Allow only the file types your application actually needs.

    Example:

    • ✅ .pdf
    • ✅ .jpg
    • ✅ .png
    • ✅ .docx

    Reject everything else.

    Remember: This is your first line of defense—not your only one.


    2. Verify the MIME Type

    Don’t trust the extension alone.

    Check the file’s MIME type sent by the client.

    For example:

    • application/pdf
    • image/jpeg
    • image/png

    Keep in mind that MIME types can also be spoofed, so continue with additional checks.


    3. Validate the File Signature (Magic Numbers)

    The most reliable validation is checking the file’s binary signature.

    For example:

    • PDF → %PDF
    • PNG → 89 50 4E 47
    • JPEG → FF D8 FF

    If the file signature doesn’t match the expected format, reject the upload.


    4. Limit File Size

    Prevent attackers from uploading extremely large files.

    Example:

    • Images: 5 MB
    • Documents: 20 MB

    This reduces the risk of storage abuse and denial-of-service attacks.


    5. Rename Uploaded Files

    Never store files using the original filename.

    Instead, generate a unique name.

    Example:

    3b8b2d1d-ef7d-4db2-8a8c-0f5d91a0f4a7.pdf
    

    This avoids filename collisions and reduces information disclosure.


    6. Store Files Outside the Web Root

    Avoid storing uploaded files in directories that can execute code.

    Instead:

    • Store files outside the web root.
    • Serve them through a controller or API after authorization.

    This prevents direct execution of uploaded files.


    7. Scan for Malware

    Integrate an antivirus solution to scan uploaded files before making them available.

    This is especially important for systems that accept documents from external users.


    8. Restrict File Permissions

    Uploaded files should never have execute permissions.

    Grant only the minimum permissions required to read or write the file.


    9. Authorize Access

    Not every uploaded file should be publicly accessible.

    Always verify that the requesting user has permission to download or view the file.


    10. Log Upload Activity

    Record important details such as:

    • User ID
    • IP address
    • File name
    • File size
    • Upload time
    • Validation failures

    Logs help detect suspicious activity and support incident investigations.


    Final Thoughts

    Secure file uploads are about defense in depth.

    Don’t rely on a single validation.

    A robust upload pipeline should include:

    • Extension validation
    • MIME type verification
    • File signature checks
    • Size limits
    • Malware scanning
    • Secure storage
    • Proper authorization

    Security isn’t a single feature—it’s a combination of small decisions that work together to protect your application.

  • User বারবার Send Button চাপল, আর Customer-এর টাকা দুইবার কেটে গেল! এটা কীভাবে প্রতিরোধ করবেন?

    User বারবার Send Button চাপল, আর Customer-এর টাকা দুইবার কেটে গেল! এটা কীভাবে প্রতিরোধ করবেন?

    ধরুন, আপনি একটি Payment API তৈরি করেছেন।

    একজন User “Pay Now” বাটনে চাপ দিলেন।

    কিন্তু Server একটু ধীর (Slow)।

    User ভাবলেন,

    “হয়তো Click হয়নি!”

    তিনি আবার Button চাপলেন।

    তারপর আবার।

    কয়েক সেকেন্ড পরে…

    Customer Support থেকে ফোন—

    “একই Payment-এর জন্য Customer-এর টাকা দুইবার কেটে গেছে!”

    এখন প্রশ্ন হলো—

    সমস্যাটা User-এর, নাকি System-এর?

    আমার মতে, System-এর।

    কারণ একটি ভালো Payment System কখনোই একই Payment একাধিকবার Process করবে না।


    প্রথম প্রতিরক্ষা: Frontend

    Frontend থেকেই Duplicate Request কমানো যায়।

    যেমন—

    • Button Click করার পর Disable করে দেওয়া।
    • Loading Spinner দেখানো।
    • Request Complete না হওয়া পর্যন্ত দ্বিতীয় Click Block করা।

    এতে অনেক Duplicate Request শুরুতেই বন্ধ হয়ে যায়।

    কিন্তু…

    শুধু Frontend-এর উপর নির্ভর করা যাবে না।

    কারণ—

    • User Page Refresh করতে পারে।
    • Network Retry হতে পারে।
    • Mobile App একই Request আবার পাঠাতে পারে।
    • কোনো Bot Request পাঠাতে পারে।

    তাই আসল নিরাপত্তা Backend-এ থাকতে হবে।


    দ্বিতীয় প্রতিরক্ষা: Idempotency Key

    প্রতিটি Payment Request-এর সাথে একটি Unique Id পাঠানো হয়।

    যেমন—

    Idempotency-Key: 7f4c9d...
    

    যদি একই Key আবার আসে,

    Server নতুন Payment Process করবে না।

    আগের Result-ই Return করবে।


    তৃতীয় প্রতিরক্ষা: Database Constraint

    Database-এ এমনভাবে Design করুন,

    যাতে একই Transaction Reference দুইবার Insert-ই করা না যায়।

    যেমন—

    • PaymentReference
    • OrderId
    • TransactionId

    এসবের উপর Unique Constraint থাকতে পারে।

    Database অনেক সময় শেষ নিরাপত্তা দেয়।


    চতুর্থ প্রতিরক্ষা: Transaction

    Payment-এর গুরুত্বপূর্ণ Operation-গুলো Transaction-এর মধ্যে করুন।

    যাতে মাঝপথে কোনো সমস্যা হলে Partial Data Save না হয়।


    পঞ্চম প্রতিরক্ষা: Distributed Lock (যেখানে প্রয়োজন)

    যদি Multiple Server একই Payment Process করতে পারে,

    তাহলে Redis Distributed Lock-এর মতো সমাধান ব্যবহার করা যেতে পারে।

    এতে একই সময়ে একটি Payment শুধুমাত্র একটি Server Process করবে।


    সবচেয়ে গুরুত্বপূর্ণ শিক্ষা

    একজন User একই Request একাধিকবার পাঠাতে পারেন।

    Network একই Request Retry করতে পারে।

    Gateway একই Callback আবার পাঠাতে পারে।

    এসবই স্বাভাবিক।

    অস্বাভাবিক হলো—

    System সেই Request-গুলোকে নতুন Payment হিসেবে ধরে আবার টাকা কেটে ফেলছে।

    একটি ভালো Payment System-এর লক্ষ্য হওয়া উচিত—

    “One business operation = One successful payment.”

    যতবার Request আসুক না কেন।

  • ম্যানেজার হঠাৎ বললেন, “এই Project-এর Estimation করে দেন!” — তখন কী করবেন?

    ম্যানেজার হঠাৎ বললেন, “এই Project-এর Estimation করে দেন!” — তখন কী করবেন?

    সকালের Stand-up Meeting শেষ।

    হঠাৎ ম্যানেজার বললেন,

    “Faiz, এই নতুন Project-এর একটা Estimation আজকেই লাগবে।”

    প্রথম প্রতিক্রিয়া কী হবে?

    অনেকেই তাড়াহুড়ো করে একটা সংখ্যা বলে দেন—

    “মনে হয়… ২ সপ্তাহ লাগবে।”

    কিন্তু একজন অভিজ্ঞ Software Engineer জানেন,

    Estimation মানে Guess করা নয়।

    Estimation মানে হলো, বর্তমান তথ্যের ভিত্তিতে একটি যৌক্তিক পূর্বাভাস (Forecast)।


    আমি কীভাবে শুরু করি?

    প্রথমেই আমি একটি প্রশ্ন করি—

    Requirement কি পুরোপুরি পরিষ্কার?

    যদি Requirement পরিষ্কার না হয়, তাহলে কোনো Estimation-ই নির্ভুল হবে না।


    এরপর Project-টাকে ছোট ছোট অংশে ভাগ করি

    একটি বড় Feature-এর Estimation করা কঠিন।

    কিন্তু যদি এটিকে ভাগ করি—

    • Authentication
    • Database Design
    • API Development
    • Frontend Integration
    • Unit Testing
    • Deployment

    তাহলে Estimation অনেক বাস্তবসম্মত হয়।


    Unknown বিষয়গুলো আলাদা করি

    সব Project-এই কিছু Unknown থাকে।

    যেমন—

    • Third-party API Integration
    • নতুন Technology
    • Client-এর পরিবর্তিত Requirement

    এসব ঝুঁকি (Risk) আগে থেকেই চিহ্নিত করলে Estimation আরও বাস্তবসম্মত হয়।


    Buffer রাখতে ভুলবেন না

    একটি Project কখনোই শুধুমাত্র Coding নয়।

    এর সাথে থাকে—

    • Code Review
    • Bug Fix
    • Testing
    • Meeting
    • Deployment
    • Production Support

    শুধু Coding Time ধরে Estimation করলে প্রায়ই Deadline মিস হয়।


    সবচেয়ে বড় ভুল

    অনেক সময় আমরা Pressure-এর কারণে বলি—

    “হ্যাঁ, হয়ে যাবে।”

    কিন্তু বাস্তবে Requirement না বুঝে দেওয়া Estimation পুরো Team-এর জন্য সমস্যার কারণ হতে পারে।

    একজন Professional Engineer প্রয়োজনে বলেন—

    “আমি Requirement Review করে আজ বিকেলের মধ্যে একটি বাস্তবসম্মত Estimation জানাচ্ছি।”

    এটা দুর্বলতা নয়, বরং Professionalism।


    আমার শেখা একটি বিষয়

    বছরের পর বছর কাজ করে একটি বিষয় বুঝেছি—

    ভুল Estimation দেওয়ার চেয়ে, একটু সময় নিয়ে সঠিক Estimation দেওয়া অনেক ভালো।

    কারণ Deadline মিস করলে শুধু Project নয়, Team-এর বিশ্বাসও ক্ষতিগ্রস্ত হয়।

    শেষ কথা

    Estimation কোনো প্রতিশ্রুতি (Promise) নয়।

    এটি হলো একটি Forecast, যা বর্তমান তথ্যের উপর ভিত্তি করে তৈরি হয়।

    আর একজন ভালো Software Engineer-এর কাজ শুধু দ্রুত উত্তর দেওয়া নয়, বরং তথ্যভিত্তিক এবং বাস্তবসম্মত উত্তর দেওয়া।

  • SQL UPDATE: Best Practices Every Developer Should Know

    SQL UPDATE: Best Practices Every Developer Should Know

    The UPDATE statement is one of the most frequently used SQL commands.

    It’s also one of the most dangerous.

    A single mistake can modify thousands—or even millions—of rows in seconds.

    That’s why every developer should understand not just how to use UPDATE, but how to use it safely.

    Basic Syntax

    UPDATE Employees
    SET Salary = 60000
    WHERE EmployeeId = 101;
    

    This updates only the employee with ID 101.


    The Most Dangerous Mistake

    Imagine running this query:

    UPDATE Employees
    SET Salary = 60000;
    

    Notice what’s missing?

    The WHERE clause.

    This statement updates every row in the table.

    In production, this mistake can be disastrous.


    Always Verify Before Updating

    Before executing an UPDATE, run the same condition with a SELECT.

    Instead of this:

    UPDATE Employees
    SET Salary = 60000
    WHERE Department = 'HR';
    

    Verify the affected rows first:

    SELECT *
    FROM Employees
    WHERE Department = 'HR';
    

    If the SELECT returns the expected rows, you’re much safer executing the update.


    Use Transactions for Large Updates

    For critical changes, wrap your update in a transaction.

    BEGIN TRANSACTION;
    
    UPDATE Employees
    SET Salary = Salary * 1.10
    WHERE Department = 'HR';
    
    -- Verify the results
    
    COMMIT;
    
    -- Or rollback if something looks wrong
    -- ROLLBACK;
    

    Transactions give you a chance to validate the changes before making them permanent.


    Update Only What You Need

    Avoid unnecessary updates.

    Instead of updating every column:

    UPDATE Employees
    SET
        Name = 'John',
        Salary = 60000,
        Department = 'IT';
    

    Update only the columns that actually changed.

    This reduces unnecessary writes and can improve performance.


    Be Careful with Joins

    SQL Server allows updates using joins.

    Example:

    UPDATE e
    SET e.ManagerId = d.ManagerId
    FROM Employees e
    JOIN Departments d
    ON e.DepartmentId = d.Id;
    

    Always verify the join with a SELECT first to ensure it returns the expected rows.


    Performance Tips

    ✔ Ensure the WHERE column is indexed.

    ✔ Avoid updating millions of rows in a single transaction when possible.

    ✔ Batch large updates to reduce locking and transaction log growth.

    ✔ Review the execution plan for expensive updates.


    Best Practices Checklist

    • ✅ Always use a WHERE clause unless you intentionally want to update every row.
    • ✅ Run a SELECT first to verify the affected rows.
    • ✅ Use transactions for critical updates.
    • ✅ Update only the required columns.
    • ✅ Test large updates in a non-production environment first.

    Final Thoughts

    Writing an UPDATE statement is easy.

    Writing a safe UPDATE statement is what separates experienced developers from beginners.

    A few extra seconds spent verifying your query can prevent hours of recovery work later.

  • Payment করার পর User-এর কাছে একাধিক Notification চলে গেল! আসল সমস্যা কোথায়?

    Payment করার পর User-এর কাছে একাধিক Notification চলে গেল! আসল সমস্যা কোথায়?

    ধরুন, আপনি একটি Payment System নিয়ে কাজ করছেন।

    একজন User সফলভাবে Payment করলেন।

    সবকিছু ঠিকঠাক।

    কিন্তু কয়েক মিনিট পর Customer Support থেকে ফোন এল—

    “একই Payment-এর জন্য Customer-এর কাছে ৫টা SMS আর ৫টা Email গেছে!”

    প্রথমে মনে হতে পারে,

    “Notification Service-এ নিশ্চয়ই Bug আছে।”

    কিন্তু একজন অভিজ্ঞ Software Engineer জানেন, সমস্যাটা শুধু Notification Service-এ নাও হতে পারে।


    প্রথমে Investigation করব

    আমি নিজেকে কয়েকটি প্রশ্ন করব—

    • Payment কি একবার হয়েছে, নাকি একাধিকবার?
    • Notification Service কি একাধিক request পেয়েছে?
    • Message Queue-তে কি একই message একাধিকবার publish হয়েছে?
    • Retry mechanism কি duplicate event তৈরি করেছে?
    • Consumer কি একই message বারবার process করেছে?

    প্রথমে Root Cause খুঁজে বের করতে হবে।


    সম্ভাব্য কারণগুলো

    ১. Duplicate API Request

    User বারবার Pay বাটনে ক্লিক করেছেন।

    অথবা Network timeout হওয়ায় Mobile App আবার একই request পাঠিয়েছে।

    সমাধান:

    Idempotency Key ব্যবহার করুন।

    একই Payment Request একাধিকবার এলেও শুধুমাত্র একটি Payment Process হবে।


    ২. Message Queue Retry

    RabbitMQ, Azure Service Bus বা Kafka অনেক সময় failed message আবার পাঠায়।

    যদি Consumer Idempotent না হয়,

    তাহলে একই Notification বারবার চলে যেতে পারে।


    ৩. Database-এ Duplicate Event

    একই Payment-এর জন্য একাধিক Event Insert হয়েছে।

    Notification Service সব Event-ই Process করেছে।


    ৪. Consumer Crash

    Consumer Notification পাঠানোর পর Crash করেছে।

    কিন্তু Message Acknowledge করার আগেই Restart হয়েছে।

    ফলে একই Message আবার Process হয়েছে।


    তাহলে সমাধান কী?

    ✅ Idempotency

    প্রতিটি Payment Event-এর একটি Unique Id রাখুন।

    যদি একই Event আবার আসে,

    সেটি Ignore করুন।


    ✅ Deduplication

    Notification পাঠানোর আগে Database-এ Check করুন—

    এই PaymentId-এর জন্য Notification ইতোমধ্যে পাঠানো হয়েছে কি না।


    ✅ Message Acknowledgement

    Notification সফলভাবে পাঠানোর পরই Message Acknowledge করুন।


    ✅ Logging & Monitoring

    প্রতিটি ধাপ Log করুন—

    • Payment Received
    • Event Published
    • Notification Sent
    • Retry Count

    এতে Root Cause খুঁজে পাওয়া অনেক সহজ হয়।


    আমার শেখা একটি বিষয়

    Distributed System-এ একটি গুরুত্বপূর্ণ নীতি হলো—

    “Network is unreliable.”

    তাই একই Request বা Event একাধিকবার আসতেই পারে।

    ভালো System সেইটিই, যা Duplicate Request পেলেও একই কাজ বারবার করে না।

    শেষ কথা

    একজন Senior Engineer-এর কাজ শুধু Bug Fix করা নয়।

    এমনভাবে System Design করা, যাতে একই সমস্যা ভবিষ্যতে আর না ঘটে।

    Payment System-এর মতো Critical Application-এ Idempotency, Deduplication এবং Proper Monitoring কোনো Luxury নয়—এগুলো Requirement।

  • Understanding the ASP.NET Core Middleware Pipeline: Why Order Matters

    Understanding the ASP.NET Core Middleware Pipeline: Why Order Matters

    One of the most common mistakes in ASP.NET Core applications isn’t writing incorrect business logic—it’s configuring the middleware pipeline in the wrong order.

    Middleware works like a series of checkpoints.

    Every incoming HTTP request passes through each middleware before reaching your controller, and the response travels back through the same pipeline in reverse.

    Request
        │
        ▼
    Serilog Request Logging
        │
        ▼
    CORS
        │
        ▼
    Swagger / Static Files
        │
        ▼
    Global Exception Handling
        │
        ▼
    HTTPS Redirection
        │
        ▼
    Routing
        │
        ▼
    Session
        │
        ▼
    Authentication
        │
        ▼
    Authorization
        │
        ▼
    Application Guard / Custom Middleware
        │
        ▼
    Controller Endpoint
        │
        ▼
    Response
    

    Let’s understand why each middleware appears where it does.


    1. Serilog Request Logging

    This should be near the beginning of the pipeline.

    Why?

    It records every incoming request along with response time, status code, and exceptions, giving you complete visibility into request processing.


    2. CORS

    CORS should run before authentication and endpoint execution.

    Its job is to determine whether a browser is allowed to access your API.

    If the request isn’t allowed, there’s no reason to continue processing it.


    3. Swagger & Static Files

    These requests usually don’t require authentication or business logic.

    Serving them early reduces unnecessary processing and improves performance.


    4. Global Exception Handling

    Unexpected exceptions can occur anywhere in the pipeline.

    By placing exception handling early, you can return consistent error responses instead of exposing internal details or crashing the application.


    5. HTTPS Redirection

    Redirect HTTP requests to HTTPS before processing sensitive operations.

    This ensures all subsequent middleware works over a secure connection.


    6. Routing

    Routing determines which endpoint matches the incoming request.

    Without routing, ASP.NET Core doesn’t know which controller or endpoint should handle the request.


    7. Session

    If your application uses sessions, initialize them before components that depend on session data.


    8. Authentication

    Authentication answers the question:

    Who is the user?

    It validates credentials such as JWT tokens or cookies and builds the user’s identity.


    9. Authorization

    Authorization answers a different question:

    Is this authenticated user allowed to perform this action?

    This is why it must come after authentication.

    Without knowing who the user is, authorization cannot make access decisions.


    10. Application Guard (Custom Middleware)

    Many applications include custom middleware for:

    • Tenant validation
    • Subscription checks
    • Maintenance mode
    • API key validation
    • Request throttling
    • Business-specific security rules

    By this stage, routing, authentication, and authorization have already been completed, making user and route information available.


    11. Controller Endpoint

    Only after all middleware checks have passed does the request reach the controller.

    The controller executes business logic, calls services, and returns a response.


    Why Middleware Order Matters

    Imagine placing UseAuthorization() before UseAuthentication().

    Authorization would execute before the user is identified, causing access checks to fail.

    Similarly, placing exception handling too late may leave some exceptions unhandled.

    Correct middleware ordering ensures:

    • Better security
    • Improved performance
    • Consistent error handling
    • Easier debugging
    • Predictable request processing

    Final Thoughts

    The middleware pipeline is the backbone of every ASP.NET Core application.

    Understanding not just the order—but the purpose of each middleware—helps you build secure, maintainable, and high-performing APIs.

    Remember:

    In ASP.NET Core, the order of middleware is just as important as the middleware itself.