How to Execute Stored Procedures With EF Core 7 - Code Maze (2024)

Want to build great APIs? Or become even better at it? Check our Ultimate ASP.NET Core Web API programand learn how to create a full production-ready ASP.NET Core API using only the latest .NET technologies. Bonus materials (Security book, Docker book, and other bonus files) are included in the Premium package!

In this article, we will see how to use stored procedures in Entity Framework Core 7.

Although Entity Framework and LINQ provide a simple way to work with the data, stored procedures still have their place when considering data access and manipulation.

To download the source code for this article, you can visit our GitHub repository.

Setup

Let’s define our data model and stored procedures.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!

We will use the same structure as in our previousarticle.

So we have a Student class:

public class Student{ public int Id { get; set; } public string? Name { get; set; } public int Mark { get; set; } public int CourseId { get; set; } public Course? Course { get; set; }}

And the Course class:

public class Course{ public int Id { get; set; } public string? Title { get; set; } public ICollection<Student>? Students { get; set; }}

We will also define our stored procedures.

First FindStudents, a procedure that returns a list of Students filtered by the name:

CREATE OR ALTER PROCEDURE [dbo].[FindStudents] @SearchFor NVARCHAR(50)ASBEGIN SET NOCOUNT ON; SELECT * FROM Students WHERE [Name] LIKE '%' + @SearchFor + '%'END

Then FindStudentsAlt, a procedure that returns a list of students with only student names and course titles filtered by student name:

CREATE OR ALTER PROCEDURE [dbo].[FindStudentsAlt] @SearchFor NVARCHAR(50)ASBEGIN SET NOCOUNT ON; SELECT StudentName = S.[Name], CourseTitle = C.Title FROM Students S LEFT JOIN Courses C ON S.CourseId = C.Id WHERE S.[Name] LIKE '%' + @SearchFor + '%'END

We will also define a procedure to update student marks, UpdateStudentMark:

CREATE OR ALTER PROCEDURE UpdateStudentMark @Id int, @Mark intASBEGIN UPDATE Students SET Mark = @Mark WHERE Id = @Id;END

And finally, we will define a procedure that updates a student’s mark and returns an average mark for all students:

CREATE OR ALTER PROCEDURE UpdateStudentMarkWithReturnValue @Id int, @Mark int, @AvgMark int OUTPUTASBEGIN UPDATE Students SET Mark = @Mark WHERE Id = @Id; SELECT @AvgMark = AVG(Mark) FROM StudentsEND

We can see that we do not use SET NOCOUNT ON; in this procedure because we want to get the number of affected rows when we call this procedure.

Using stored procedures can simplify and improve the execution of SQL queries. We can use stored procedures to define complex queries which might become too complicated to read and write in LINQ.

If we want to use a stored procedure to query the data, the query needs to return the complete set of properties (columns) for EF to map the data to the entity.

Stored procedures in EF 7

We can divide methods to execute stored procedures in EF into two groups, methods to query the data and methods to add/update/delete data.

If we want to query the data, we will use FromSql, FromSqlInterpolated, or FromSqlRaw methods that are available only on the DbSet object.

To add, update or remove records, we need to use ExecuteSql, ExecuteSqlInterpolated, or ExecuteSqlRaw methods, which are available for DbContext.Database object.

Let’s take a closer look at these methods.

DbSet.FromSql

Let’s define a method FindStudentsFromSql that uses the FromSql method to call FindStudents stored procedure and returns a list of Students:

public static List<Student>? FindStudentsFromSql(AppDbContext context, string searchFor){ return context?.Students?.FromSql($"FindStudents {searchFor}").ToList();}

FromSql method is introduced in EF Core 7.0. It takes FormattableString as the only parameter, which means it is safe against SQL injection since all parameter data are wrapped in an DbParameter object. As a parameter string, we pass the name of the procedure and the value for the parameter.

DbSet.FromSqlInterpolated

FromSqlInterpolated is s available since EF Core 3.0, and it is basically the same as FromSql:

public static List<Student>? FindStudentsFromSqlInterpolated(AppDbContext context, string searchFor){ return context?.Students?.FromSqlInterpolated($"FindStudents {searchFor}").ToList();}

Next, when we call the FindStudentsAlt stored procedure, we cannot map the result to our data model:

public static List<Student>? FindStudentsAltFromSqlInterpolated(AppDbContext context, string searchFor){ return context?.Students?.FromSqlInterpolated($"FindStudentsAlt {searchFor}").ToList();}

As a result, we see it will throw an exception since the underlying stored procedure returns an incomplete set of results:

public void WhenFindStudentsAltFromSqlInterpolated_ThenSuccess(){ Assert.Throws<InvalidOperationException>( () => Methods.FindStudentsAltFromSqlInterpolated(context, "100") );}

DbSet.FromSqlRaw

Unlike FromSql and FromSqlInterpolated, FromSqlRaw is not safe from SQL injection attacks. Therefore, we need to pay extra attention when defining SQL commands.

We should never pass concatenated or interpolated strings into this method.

Let’s define how we can use it to call stored procedure:

public static List? FindStudentsFromSqlRaw(AppDbContext context, string searchFor){ return context?.Students?.FromSqlRaw("FindStudents @searchFor", new SqlParameter("@searchFor", searchFor)).ToList();}

Let’s now show how we can misuse this method and, therefore, abuse it to perform SQL injection attacks.

First, we need to define our unsafe method:

public static List<Student>? FindStudentsFromSqlRawUnsafe(AppDbContext context, string searchFor){ return context?.Students?.FromSqlRaw($"FindStudents @searchFor = '{searchFor}'").ToList();}

Here we can see that we call the correct stored procedure, but the @searchFor parameter is not passed as a DbParameter and the SQL command is constructed using interpolated string.

We can still use the method safely:

public void WhenFindStudentsExecuteSqlRawUnsafe_ThenSuccess(){ var results = FindMethods.FindStudentsFromSqlRawUnsafe(context, "100"); Assert.True(results?.Count == 9);}

But we can also use it maliciously:

public void WhenFindStudentsExecuteSqlRawUnsafeSqlInjection_ThenSuccess(){ var results = FindMethods.FindStudentsFromSqlRawUnsafe(context, @"xyz'; UPDATE Students SET Name = 'Student 000' WHERE Id = 1; SELECT '"); Assert.True(results?.Count == 0);}

This example will not return any Students, but it will execute an UPDATE (or any other) query and possibly compromise the whole database.

Database.ExecuteSql

As we already mentioned, if we need to perform insert, update, or delete operations, we will use ExecuteSql methods on DbContext.Database object.

Here is how we can use the ExecuteSql method to call our UpdateStudentMark stored procedure:

public static int UpdateStudentMarkSql(AppDbContext context, int id, int mark){ return context.Database.ExecuteSql($"UpdateStudentMark @Id={id}, @Mark={mark}");}

We can also use the async version:

public async static Task<int> UpdateStudentMarkSqlAsync(AppDbContext context, int id, int mark){ return await context.Database.ExecuteSqlAsync($"UpdateStudentMark @Id={id}, @Mark={mark}");}

Same as FromSql and FromSqlInterpolated methods, ExecuteSql and ExecuteSqlInterpolated methods are safe against SQL injection attacks.

Database.ExecuteSqlInterpolated

Usage of the ExecuteSqlInterpolated method is the same as for ExecuteSql:

public static int UpdateStudentMarkSqlInterpolated(AppDbContext context, int id, int mark){ return context.Database.ExecuteSqlInterpolated($"UpdateStudentMark @Id={id}, @Mark={mark}");}

And the async version:

public async static Task<int> UpdateStudentMarkSqlInterpolatedAsync(AppDbContext context, int id, int mark){ return await context.Database.ExecuteSqlInterpolatedAsync($"UpdateStudentMark @Id={id}, @Mark={mark}");}

Database.ExecuteSqlRaw

And again, the same as FromSqlRaw, the ExecuteSqlRaw method needs to be used with caution for the same reasons.

Here we see how we can use it properly with both sync:

public static int UpdateStudentMarkSqlRaw(AppDbContext context, int id, int mark){ return context.Database.ExecuteSqlRaw("dbo.UpdateStudentMark @Id, @Mark", new SqlParameter("@Id", id), new SqlParameter("@Mark", mark));}

And async version:

public async static Task UpdateStudentMarkSqlRawAsync(AppDbContext context, int id, int mark){ return await context.Database.ExecuteSqlRawAsync("dbo.UpdateStudentMark @Id, @Mark", new SqlParameter("@Id", id), new SqlParameter("@Mark", mark));}

Let’s see one example of calling a stored procedure with a return value. For this, a stored procedure needs to have an output parameter defined, and we need to define an output SQL parameter when we call that procedure:

public static int UpdateStudentMarkWithReturnValueSqlRaw(AppDbContext context, int id, int mark){ var avgMarkParameter = new SqlParameter() { ParameterName = "AvgMark", SqlDbType = System.Data.SqlDbType.Int, Direction = System.Data.ParameterDirection.Output }; context.Database.ExecuteSqlRaw("dbo.UpdateStudentMarkWithReturnValue @Id, @Mark, @AvgMark OUTPUT", new SqlParameter("@Id", id), new SqlParameter("@Mark", mark), avgMarkParameter); return (int)avgMarkParameter.Value;}

Dynamic SQL

Now that we know how to use all these different methods to execute SPs, we are going to show a few more features that they support, which are not so much related only to stored procedures.

Let’s consider a situation where we need to create the query dynamically. It might seem like the interpolated string is the way to go:

public static int UpdateStudentMarkSqlDynamic(AppDbContext context, int id, int mark){ var field1 = "@Id"; var field2 = "@Mark"; return context.Database.ExecuteSql($"UpdateStudentMark {field1}={id}, {field2}={mark}");}

But this is not possible because databases do not allow parameterizing parameter names or any other part of the schema, and this method would throw an SqlException.

In this situation, we can use the raw method to create the query:

public static int UpdateStudentMarkSqlRawDynamic(AppDbContext context, int id, int mark){ var field1 = "@Id"; var field2 = "@Mark"; return context.Database.ExecuteSqlRaw($"dbo.UpdateStudentMark {field1} = @Id, {field2}=@Mark", new SqlParameter("@Id", id), new SqlParameter("@Mark", mark));}

Here we still want to send input values as SqlParameter objects to prevent possible SQL injection attacks.

Combining With LINQ

It is worth pointing out it is possible to combine SQL commands with LINQ operators.

This is not directly applicable to the stored procedure, so we will define a method that uses a simple SELECT query in combination with LINQ:

public static List? FindStudentsFromSqlAndLinq(AppDbContext context, string searchFor){ return context?.Students? .FromSql($"SELECT * FROM Students") .Where(m => m.Name != null && m.Name.IndexOf(searchFor) > -1) .OrderBy(m => m.Mark) .ToList();}

We can also inspect the SQL query this method generates:

DECLARE @__searchFor_1 nvarchar(4000) = N'100';SELECT [e].[Id], [e].[CourseId], [e].[Mark], [e].[Name] FROM ( SELECT * FROM Students ) AS [e]WHERE ([e].[Name] IS NOT NULL) AND CASE WHEN @__searchFor_1 = N'' THEN 0ELSE CAST(CHARINDEX(@__searchFor_1, [e].[Name]) AS int) - 1 END > -1ORDER BY [e].[Mark]

We see that LINQ queries are applied on top of the initial SQL query.

Change Tracking

Queries that use the methods we just described follow the same change tracking rules as LINQ queries. EF tracks these results by default, as it tracks any other result from a LINQ query.

Let’s define a method that demonstrates this:

public static int? FindStudentsFromSqlAndUpdateMarks(AppDbContext context, string searchFor){ var students = context?.Students? .FromSql($"FindStudents {searchFor}").ToList(); if (students != null) foreach (var student in students) student.Mark += 1; return context?.SaveChanges();}

The results we get from the stored procedure are valid entities, and we can work with them as with any other collection of entities.

In this case, the Context.SaveChanges method will successfully save updated data to the database.

Conclusion

In this article, we learned how to work with stored procedures from EF Core 7.0.

We also examined the limitations and possible security pitfalls when executing stored procedures through available EF Core methods.

Want to build great APIs? Or become even better at it? Check our Ultimate ASP.NET Core Web API programand learn how to create a full production-ready ASP.NET Core API using only the latest .NET technologies. Bonus materials (Security book, Docker book, and other bonus files) are included in the Premium package!

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!

How to Execute Stored Procedures With EF Core 7 - Code Maze (2024)
Top Articles
Asna 6127
Carespring Health Care Management hiring STNA (State Tested Nursing Assistant) | FT/PT/PRN in Cincinnati, Ohio, United States | LinkedIn
The UPS Store | Ship & Print Here > 400 West Broadway
Monthly Forecast Accuweather
East Cocalico Police Department
Www.politicser.com Pepperboy News
Txtvrfy Sheridan Wy
Routing Number 041203824
Pj Ferry Schedule
Monticello Culver's Flavor Of The Day
Scentsy Dashboard Log In
What is a basic financial statement?
Immediate Action Pathfinder
Nebraska Furniture Tables
Craigslist Edmond Oklahoma
The best TV and film to watch this week - A Very Royal Scandal to Tulsa King
Odfl4Us Driver Login
Apply for a credit card
Sprinkler Lv2
97226 Zip Code
Exterior insulation details for a laminated timber gothic arch cabin - GreenBuildingAdvisor
Hdmovie2 Sbs
Shiftselect Carolinas
Gina Wilson All Things Algebra Unit 2 Homework 8
UMvC3 OTT: Welcome to 2013!
Rust Belt Revival Auctions
Dark Entreaty Ffxiv
Urban Dictionary Fov
Ou Football Brainiacs
Mikayla Campinos: Unveiling The Truth Behind The Leaked Content
Access a Shared Resource | Computing for Arts + Sciences
Pdx Weather Noaa
Emily Katherine Correro
Metra Union Pacific West Schedule
Navigating change - the workplace of tomorrow - key takeaways
Bbc Gahuzamiryango Live
Duff Tuff
How To Get Soul Reaper Knife In Critical Legends
Setx Sports
Garland County Mugshots Today
Patricia And Aaron Toro
Enr 2100
17 of the best things to do in Bozeman, Montana
Craigslist Free Cats Near Me
Mmastreams.com
Fresno Craglist
Uno Grade Scale
Glowforge Forum
Craigslist Monterrey Ca
Convert Celsius to Kelvin
Palmyra Authentic Mediterranean Cuisine مطعم أبو سمرة
Factorio Green Circuit Setup
Latest Posts
Article information

Author: Melvina Ondricka

Last Updated:

Views: 5689

Rating: 4.8 / 5 (48 voted)

Reviews: 87% of readers found this page helpful

Author information

Name: Melvina Ondricka

Birthday: 2000-12-23

Address: Suite 382 139 Shaniqua Locks, Paulaborough, UT 90498

Phone: +636383657021

Job: Dynamic Government Specialist

Hobby: Kite flying, Watching movies, Knitting, Model building, Reading, Wood carving, Paintball

Introduction: My name is Melvina Ondricka, I am a helpful, fancy, friendly, innocent, outstanding, courageous, thoughtful person who loves writing and wants to share my knowledge and understanding with you.