Answered by:
Insert, Update and Delete Using SQL Parameterised Stored Procedures in ASP.NET MVC

Question
-
User-164452226 posted
Good day tech gurus,
l have been able to save, update and delete using Entity Framework but without procedures. l find procedures to be very handy and fast and now want to use them. Could anyone please assist me on how l can employ stored procedures on my design. Here is an example of a functional design which l would be happy if anyone employs stored procedures without diverting me from the design pattern:
1) Courses table from SQL Server 2017
CREATE TABLE [dbo].[Courses]( [CourseID] [int] IDENTITY(1,1) NOT NULL, [CourseCode] [nvarchar](50) NOT NULL, [CourseName] [nvarchar](50) NULL, CONSTRAINT [PK_Courses_1] PRIMARY KEY CLUSTERED
2) Procedures to save, update and delete
Save and Update proc create proc sp_SaveCourse ( @CourseID int, @CourseCode nvarchar(50), @CourseName nvarchar(50) ) as if (@CourseID<=0) insert into Courses ( CourseCode, Coursename ) values ( @CourseCode, @CourseName ) else update Courses set CourseCode=@CourseCode, CourseName=@CourseName where CourseID=@CourseID Delete proc create proc sp_DeleteCourse( @CourseID int ) as delete from Courses where CourseID=@CourseID
3) My ASP.Net MVC
Course Class in CollegeSys.Domain project under entities
namespace CollegeSys.Domain.Entities { public class Course { [Key] public int CourseID { get; set; } public string CourseCode { get; set; } public string CourseName { get; set; } } }
4) My Repository class that links with the Entities
using CollegeSys.Domain.Entities; namespace CollegeSys.Domain.Abstract { public interface ICourseRepository { IEnumerable<Course> Courses { get; } } }
5) My DBContext Class that handles DB
using System.Data.Entity; using CollegeSys.Domain.Entities; using System.ComponentModel.DataAnnotations; namespace CollegeSys.Domain.Concrete { public class EFDbContext : DbContext { public DbSet<Course> Courses { get; set; } } }
6) My Entity Framework data access methods for saving, updating and deleting (This is were l want to use stored procedures)
using CollegeSys.Domain.Abstract; using CollegeSys.Domain.Entities; namespace CollegeSys.Domain.Concrete { public class EFCourseRepository : ICourseRepository { private EFDbContext context = new EFDbContext(); public IEnumerable<Course> Courses { get { return context.Courses; } } public void SaveCourse(Course course)//REPLACE WITH sp_SaveCourses { if (course.CourseID == 0) { context.Courses.Add(course); } else { Course dbEntry = context.Courses.Find(course.CourseID); if (dbEntry != null) { dbEntry.CourseCode = course.CourseCode; dbEntry.CourseName = course.CourseName; } } context.SaveChanges(); } public Course DeleteCourse (int CourseID) //REPLACE WITH sp_DeleteCourses { Course dbEntry = context.Courses.Find(CourseID); if (dbEntry != null) { context.Courses.Remove(dbEntry); context.SaveChanges(); } return dbEntry; } } }
7) My Ninject Infrastructure - binds the repository to the entity framework and its under a second project, CollegeSys.WebUI
using Ninject; using CollegeSys.Domain.Abstract; using CollegeSys.Domain.Concrete; using CollegeSys.Domain.Entities; namespace CollegeSys.WebUI.Infrastructure { public class NinjectDependencyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependencyResolver(IKernel kernelParam) { kernel = kernelParam; AddBindings(); } public object GetService(Type serviceType) { return kernel.TryGet(serviceType); } public IEnumerable<object> GetServices(Type serviceType) { return kernel.GetAll(serviceType); } private void AddBindings() { // put bindings here kernel.Bind<ICourseRepository>().To<EFCourseRepository>(); } } }
8) My Course Controller under CollegeSys.WebUI
using CollegeSys.Domain.Abstract; using CollegeSys.Domain.Concrete; using CollegeSys.WebUI.Models; using CollegeSys.Domain.Entities; namespace CollegeSys.WebUI.Controllers { public class CourseController : Controller { EFCourseRepository repository = new EFCourseRepository(); public CourseController(EFCourseRepository collegeRepository) { this.repository = collegeRepository; } public ViewResult Index() { return View(repository.Courses); } public ViewResult List() { CoursesListViewModel model = new CoursesListViewModel { Courses = repository.Courses.OrderBy(c => c.CourseCode) }; return View(model); } public ViewResult Edit(int courseID) { Course course = repository.Courses .FirstOrDefault(c => c.CourseID == courseID); return View(course); } [HttpPost] public ActionResult Edit(Course course) { if (ModelState.IsValid) { repository.SaveCourse(course); TempData["message"] = string.Format("{0} has been saved", course.CourseName); return RedirectToAction("Index"); } else { // there is something wrong with the data values return View(course); } } public ViewResult Create() { return View("Edit", new Course()); } [HttpPost] public ActionResult Delete(int CourseID) { Course DeleteCourse = repository.DeleteCourse(CourseID); if (DeleteCourse != null) { TempData["message"] = string.Format("{0} was deleted", DeleteCourse.CourseName); } return RedirectToAction("Index"); } public CourseController() { } } }
Please help on how l can use the stored procedures without changing much of my design pattern. Thank you
Thursday, October 25, 2018 6:35 PM
Answers
-
User1520731567 posted
Hi tmatiwure,
If you would like to employ stored procedures on your repository pattern design,
I suggest you could create q class specifically for handling stored procedures.
You could refer to this article,hope it helpful to you:
Best Regards.
Yuki Tao
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, October 26, 2018 7:34 AM
All replies
-
User475983607 posted
Executing stored procedures using Entity Framework is explained here.
https://www.mikesdotnetting.com/article/299/entity-framework-code-first-and-stored-procedures
I don't see how invoking a proc will impact your current design. Can you explain the problem you are having while moving to stored procedures? Please post sample code.
Thursday, October 25, 2018 7:22 PM -
User753101303 posted
Hi,
Or you have https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/fluent/cud-stored-procedures
Start with a separate SP for each and note that you need to return db generated value (such as the pk for the newly inserted row). Also you just tell EP to call your SP rarther than to generate SQL statements but beyond that you shouldn't have anything to change.
Thursday, October 25, 2018 8:05 PM -
User1120430333 posted
l find procedures to be very handy and fast and now want to use them.
EF uses the MS SQL Server internal stored procedure for the generated T-TQL that the EF engine submits for execution to the DB engine.
My Entity Framework data access methods for saving, updating and deleting (This is were l want to use stored procedures)
It's really not buying you anything, becuase EF uses the internal stored procedure. The reasons to use a sproc would be to do something that the ORM cannot do with the generated T-SQL it creates, IMO.
You are not using a generic repository so you could return DTO(s).
https://juristr.com/blog/2012/10/lessions-learned-dont-expose-ef-entities-to-the-client-directly/
https://www.codeproject.com/Articles/1050468/Data-Transfer-Object-Design-Pattern-in-Csharp
On the other hand, you are doing something with the repository that it shouldn't be doing.
https://programmingwithmosh.com/entity-framework/common-mistakes-with-the-repository-pattern/
You could look at the DAO pattern.
https://en.wikipedia.org/wiki/Data_access_object
https://blog.sapiensworks.com/post/2012/11/01/Repository-vs-DAO.aspx
https://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm
Friday, October 26, 2018 4:11 AM -
User-821857111 posted
create proc sp_SaveCourseDon't do that. Don't use sp_ as a prefix on your own stored procedures: https://sqlperformance.com/2012/10/t-sql-queries/sp_prefixFriday, October 26, 2018 6:52 AM -
User1520731567 posted
Hi tmatiwure,
If you would like to employ stored procedures on your repository pattern design,
I suggest you could create q class specifically for handling stored procedures.
You could refer to this article,hope it helpful to you:
Best Regards.
Yuki Tao
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, October 26, 2018 7:34 AM -
User-164452226 posted
Executing stored procedures using Entity Framework is explained here.
https://www.mikesdotnetting.com/article/299/entity-framework-code-first-and-stored-procedures
I don't see how invoking a proc will impact your current design. Can you explain the problem you are having while moving to stored procedures? Please post sample code.
l modified my EF as follows
using CollegeSys.Domain.Abstract; using CollegeSys.Domain.Entities; namespace CollegeSys.Domain.Concrete { public class EFCourseRepository : ICourseRepository { private EFDbContext context = new EFDbContext(); public IEnumerable<Course> Courses { get { return context.Courses; } } public void AddCourse(Course course) { context.Courses.SqlQuery("sp_SaveCourse @CourseID, @CourseCode, @CourseName", course.CourseID, course.CourseCode, course.CourseName); } public void DeleteCourse(Course course) { context.Courses.SqlQuery("sp_DeleteCourse @CourseID", course.CourseID); } } }
My controller now look as follows
using CollegeSys.Domain.Abstract; using CollegeSys.Domain.Concrete; using CollegeSys.WebUI.Models; using CollegeSys.Domain.Entities; namespace CollegeSys.WebUI.Controllers { public class CourseController : Controller { EFCourseRepository repository = new EFCourseRepository(); public CourseController(EFCourseRepository collegeRepository) { this.repository = collegeRepository; } public ViewResult Index() { return View(repository.Courses); } [HttpPost] public ActionResult Add(Course course) { if (ModelState.IsValid) { repository.AddCourse(course); TempData["message"] = string.Format("{0} has been saved", course.CourseName); return RedirectToAction("Index"); } else { // there is something wrong with the data values return View(course); } } public ViewResult CreateCourse() { return View("Add", new Course()); } [HttpPost] public ActionResult Delete(Course course) { repository.DeleteCourse(course); return RedirectToAction("Index"); } public CourseController() { } }
My Index View
@model IEnumerable<CollegeSys.Domain.Entities.Course> @{ ViewBag.Title = "Admin: All Courses"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div class="panel panel-default"> <div class="panel-heading"> <h3>All Courses</h3> </div> <div class="panel-body"> <table class="table table-striped table-condensed table-bordered"> <tr> <th class="text-right">ID</th> <th>Name</th> <th class="text-center">Actions</th> </tr> @foreach (var item in Model) { <tr> <td class="text-right">@item.CourseID</td> <td>@Html.ActionLink(item.CourseName, "Edit", new { item.CourseID })</td> <td class="text-center"> @using (Html.BeginForm("Delete", "Course")) { @Html.Hidden("CourseID", item.CourseID) <input type="submit" class="btn btn-default btn-xs" value="Delete" /> } </td> </tr> } </table> </div> <div class="panel-footer"> @Html.ActionLink("Add a new course", "CreateCourse", null, new { @class = "btn btn-default" }) </div> </div>
My Add View
@model CollegeSys.Domain.Entities.Course @{ ViewBag.Title = "Add" + @Model.CourseName; Layout = "~/Views/Shared/_Layout.cshtml"; } <div class="panel"> <div class="panel-heading"> <h3>Add @Model.CourseName</h3> </div> @using (Html.BeginForm("Add", "Course")) { <div class="panel-body"> @Html.HiddenFor(c => c.CourseID) @foreach (var property in ViewData.ModelMetadata.Properties) { if (property.PropertyName != "CourseID") { <div class="form-group"> <label>@(property.DisplayName ?? property.PropertyName)</label> @Html.TextBox(property.PropertyName, null, new { @class = "form-control" }) </div> } } </div> <div class="panel-footer"> <input type="submit" value="Save" class="btn btn-primary" /> @Html.ActionLink("Cancel and return to List", "Index", null, new { @class = "btn btn-default" }) </div> } </div>
l can't add or delete a course. No error message returned.
Friday, October 26, 2018 8:29 AM -
User-164452226 posted
tmatiwure
create proc sp_SaveCourseDon't do that. Don't use sp_ as a prefix on your own stored procedures: https://sqlperformance.com/2012/10/t-sql-queries/sp_prefix
Noted
Friday, October 26, 2018 8:36 AM -
User-821857111 posted
At a quick glance, in respect of your Delete method, it expects a Course object as a parameter. You are only passing the ID. Change the parameter to an int (which is all you need) and make the same change in the repo method.
Also ,if you aren't using the SQL generation capabilities of EF and performance is main concern for you, perhaps you should consider using Dapper for your data access layer instead of EF.
Friday, October 26, 2018 8:56 AM -
User-164452226 posted
At a quick glance, in respect of your Delete method, it expects a Course object as a parameter. You are only passing the ID. Change the parameter to an int (which is all you need) and make the same change in the repo method.
Also ,if you aren't using the SQL generation capabilities of EF and performance is main concern for you, perhaps you should consider using Dapper for your data access layer instead of EF.
l am new to ASP.Net, this is why l take time to post detailed code so you can assist me practically. Is this the correct way to pass parameters?
using System.Data.SqlClient; public void AddCourse(Course course) { context.Courses.SqlQuery("sp_SaveCourse @CourseID, @CourseCode, @CourseName", new SqlParameter("CourseID", course.CourseID), new SqlParameter("CourseCode", course.CourseCode), new SqlParameter("CourseName", course.CourseName) ); }
Friday, October 26, 2018 9:46 AM -
User1120430333 posted
l am new to ASP.Net, this is why l take time to post detailed code so you can assist me practically. Is this the correct way to pass parameters?
Why are you not using ADO.NET, SQL Command Objects, parmterized stored procedure, a datareader and the DTO pattern for CRUD with the database, or use a micro ORM like Dapper if all you are doing is running stored procedures?
What value is EF providing if all you are running are stored procedures?
Friday, October 26, 2018 10:09 AM -
User-821857111 posted
tmatiwure
Is this the correct way to pass parameters?Yes it is, but you must ensure that the value you post from the page matches the parameter that your method expects.
tmatiwure
public ActionResult Delete(Course course)
{
repository.DeleteCourse(course);
return RedirectToAction("Index");
}
[/quote]
This method expects a course object.
tmatiwure
@using (Html.BeginForm("Delete", "Course"))
{
@Html.Hidden("CourseID", item.CourseID)
<input type="submit" class="btn btn-default btn-xs" value="Delete" />
}This form will only post an int, not a course object.
Friday, October 26, 2018 10:39 AM -
User753101303 posted
You are just calling explicitely stored procedures.
If you actually look at the previous posted links you'll see that you can configure EF so that EF will call stored procedures automatically for you rather than generating SQL statements.
Beyond that configuration change your EF code will be just left unchanged.
Friday, October 26, 2018 10:39 AM -
User-164452226 posted
tmatiwure
Is this the correct way to pass parameters?Yes it is, but you must ensure that the value you post from the page matches the parameter that your method expects.
tmatiwure
public ActionResult Delete(Course course)
{
repository.DeleteCourse(course);
return RedirectToAction("Index");
}
[/quote]
This method expects a course object.
tmatiwure
@using (Html.BeginForm("Delete", "Course"))
{
@Html.Hidden("CourseID", item.CourseID)
<input type="submit" class="btn btn-default btn-xs" value="Delete" />
}This form will only post an int, not a course object.
[HttpPost] public ActionResult Delete(int courseID) { repository.DeleteCourse(courseID); return RedirectToAction("Index"); }
Still no joy for me. You guys seem to understand this staff much better. l wish if you could modify my example l posted above and that way l will learn much better. l am noticing also that there is a problem in the way l am passing parameters. For instance:
Int64 testId = Convert.ToInt64(HttpContext.Session["testId"]); var TestMark = context.TpSchoolStudentMarks.SqlQuery("spStudentMarks @TestID", new SqlParameter("testId", testId));
When l execute that procedure in sql server, it's working but on this particular method where l am so sure that the session has been captured and converted to int, nothing is happening.
Friday, October 26, 2018 1:32 PM -
User-164452226 posted
You are just calling explicitely stored procedures.
If you actually look at the previous posted links you'll see that you can configure EF so that EF will call stored procedures automatically for you rather than generating SQL statements.
Beyond that configuration change your EF code will be just left unchanged.
Are you saying that l should not write my own SQL Statements in stored procedures l am trying to call and rely on EF Procedures or you are saying EF can call my procedures automatically? l am so new to ASP.Net and would much appreciate if you can just show me an example based on the short program code l posted. l have read so much material that some of it is confusing me. Some material will only make sense if l have been shown practically based on my example. l am trying my best to go through the links though
Friday, October 26, 2018 1:41 PM -
User-164452226 posted
DA924
l am new to ASP.Net, this is why l take time to post detailed code so you can assist me practically. Is this the correct way to pass parameters?
Why are you not using ADO.NET, SQL Command Objects, parmterized stored procedure, a datareader and the DTO pattern for CRUD with the database, or use a micro ORM like Dapper if all you are doing is running stored procedures?
What value is EF providing if all you are running are stored procedures?
l am trying to separate concerns for easier trouble shooting. l don't want to end up doing spaghetti programming where l just bunch everything in one class/controller. I think it's best for a learner like me to be in a position to know that this class is handling data and this one is a middle tier and so forth.
Friday, October 26, 2018 1:58 PM -
User-164452226 posted
Hi tmatiwure,
If you would like to employ stored procedures on your repository pattern design,
I suggest you could create q class specifically for handling stored procedures.
You could refer to this article,hope it helpful to you:
Best Regards.
Yuki Tao
Finally, l got it. Thanks, thanks and many thanks
Friday, October 26, 2018 3:36 PM