HomeNikola Knezevic

In this article

Banner

Prevent SQL Injection in .NET

11 Jun 2026
5 min

Special Thanks to Our Sponsors:

Sponsor Logo

EF Core is too slow? Discover how you can easily insert 14x faster (reducing saving time by 94%).

Boost your performance with our method within EF Core: Bulk Insert, update, delete and merge.

Thousands of satisfied customers have trusted our library since 2014.

👉 Learn more

Sponsor Newsletter

SQL injection is still one of the easiest vulnerabilities to introduce in an application.

Despite modern frameworks providing safer defaults, SQL Injection vulnerabilities still appear in production applications, often due to a lack of understanding of how database queries are executed.

In this article we will look at what SQL injection is, how it is exploited and how to prevent it with EF Core and Dapper.

What Is SQL Injection

SQL injection lets an attacker change the query your application sends to the database.

Consider the following example:

csharp
using var connection = connectionFactory.OpenConnection();

var order = await connection.QueryFirstOrDefaultAsync<Order>(
    $"SELECT * FROM "Orders" WHERE "SerialNumber" = '{serialNumber}'");

At first glance, this may seem harmless. However, if an attacker submits the following value:

sql
SELECT * FROM "Orders" WHERE "SerialNumber" = '' OR 1=1 --'

The OR 1=1 condition is always true, so the query can return every order instead of one lookup. The -- comments out the rest of the line.

sql
SELECT * FROM "Orders" WHERE "SerialNumber" = 'anything'; DELETE FROM "Orders"; --

At this point, the database is no longer executing a simple lookup, it's executing arbitrary commands and the attacker can potentially read, modify or delete data.

The fix is straightforward, use parameterized queries so user input is always sent as a value, never as part of the command.

Prevent SQL Injection with EF Core

The safest default in EF Core is LINQ. The provider translates your query into parameterized SQL, so user input is not embedded in the command text:

csharp
var order = await dbContext.Orders
    .FirstOrDefaultAsync(o => o.SerialNumber == serialNumber);

Even though serialNumber comes from the user, EF Core sends it as a SQL parameter:

sql
SELECT o."Id", o."SerialNumber", ...
FROM "Orders" AS o
WHERE o."SerialNumber" = @__serialNumber_0

This means that regardless of what the user inputs, it's always treated as a value, not part of the SQL query.

When you need raw SQL, simply use placeholders with FromSqlRaw:

csharp
var orders = await dbContext.Orders
    .FromSqlRaw(
        "SELECT * FROM "Orders" WHERE "SerialNumber" = {0}",
        serialNumber)
    .ToListAsync();

NOTE: Placeholders like {0} are replaced with parameters at execution time. Do not build the SQL string with $"..." or + for values that come from the user.

For most scenarios, sticking to LINQ keeps you on the safe path without thinking about SQL syntax at all.

Prevent SQL Injection with Dapper

With Dapper, you write the SQL yourself, so parameterization is your responsibility. Use named parameters and pass values in a separate object:

csharp
const string sql = """
    SELECT *
    FROM "Orders"
    WHERE "SerialNumber" = @SerialNumber
    """;

var order = await connection.QueryFirstOrDefaultAsync<Order>(
    sql,
    new { SerialNumber = serialNumber });

Dapper maps @SerialNumber to the property on the anonymous object and sends it as a parameter, not as part of the SQL string.

The same pattern works for inserts, updates and stored procedures.

For more on setting up Dapper in ASP.NET Core, see my post on Dapper in ASP.NET Core.

Conclusion

SQL injection is still a real risk in .NET because the problem is how SQL is built, not which ORM you pick.

With EF Core, prefer LINQ and parameterized raw SQL. With Dapper, always use named parameters and keep user input out of the query string.

That small habit protects your data and keeps your API trustworthy as it grows.

If you want to check out examples I created, you can find the source code here:

Source Code

I hope you enjoyed it, subscribe and get a notification when a new blog is up!

Subscribe

Stay tuned for valuable insights every Thursday morning.