Stored procedures with ef 6 and code first - entity-framework

I am quite new to EF and MVC. But I have managed to create scaffolded pages with MCV5 and views.
I understand there is difference in concept between webforms and EF. But is it possible for me to execute a stored procedure that is created in SSMS.
I want the ability of have a webpage with two text boxes that take in the two in parameters the stored procedure requires, both of the parameters being strings and then display the result in a webgrid.
Is this doable with EF code first and MVC?
The code for my stored procedure is:
CREATE PROCEDURE [dbo].[sp_FindRoutes]
#DepCity nvarchar(max),
#ArvCity nvarchar(max)
AS
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT dbo.Airlines.AirlineName, dbo.Routes.DepCity, dbo.Routes.ArvCity, dbo.Routes.FlightNr, dbo.AirlineRoutes.Fare, dbo.AirlineRoutes.AircraftAllocated
FROM dbo.Routes INNER JOIN
dbo.AirlineRoutes ON dbo.Routes.RouteId = dbo.AirlineRoutes.RouteId INNER JOIN
dbo.Airlines ON dbo.AirlineRoutes.AirlineId = dbo.Airlines.AirlineId
WHERE dbo.Routes.DepCity LIKE #DepCity AND dbo.Routes.ArvCity LIKE #ArvCity;
GO

Let's say your SP is like this (Just a simple version of your SP).
CREATE PROCEDURE [dbo].[sp_FindRoutes]
#DepCity string
AS
BEGIN
SET NOCOUNT ON;
SELECT dbo.Airlines.AirlineName, dbo.Routes.DepCity, dbo.Routes.ArvCity
FROM dbo.Routes
WHERE dbo.Routes.DepCity= #DepCity
END
You have to create an object that has the same property names as the results returned by the stored procedure.It's like this.
public class RouteForAirline
{
public string AirlineName { get; set; }
public string DepCity { get; set; }
public string ArvCity { get; set; }
}
Then call your SP as shown below.
Note : You can put this on the class file if you can access the DatabaseContext().
using(var context = new DatabaseContext())
{
var depCityParameter = new SqlParameter("#DepCity", "colombo");
var arvCityParameter = new SqlParameter("#ArvCity", "matara");
var results = context.Database.SqlQuery<RouteForAirline>("sp_FindRoutes #DepCity, #ArvCity",depCityParameter,arvCityParameter).ToList();
}
You can read more about this using below mentioned links.
Code First Insert/Update/Delete Stored Procedures
Stored Procedures with Multiple Result Sets

Related

Calling a simple stored procedure in EF core 3.1

I have a very simple stored procedure:
CREATE PROCEDURE [dbo].[ClearIterations]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
delete from iterations
END
GO
When calling it from EF it is not called. I get no errors:
public void ClearIterations()
{
this.Iterations.FromSqlRaw("ClearIterations").IgnoreQueryFilters();
}
Any pointers? I found the sample above on another thread in here that where the code above is the answer. It seems kind of strange I have to call this this.Iterations to call a SP.
EF Core 3.x+ provides two raw SQL sets of methods - FromSql and ExecuteSql, both with Raw / Interpolated and Async versions.
The former are used for querying. They return IQueryable<T>, allow query composition and as any LINQ query are not executed until the result is enumerated.
While the later is used to immediately execute arbitrary SQL (DDL, DML, batch etc.). They are EF Core equivalent of ADO.NET ExecuteNonQuery and return the records affected. Output (or input/output) primitive value parameters can be used to obtain the results.
Shortly, ExecuteSql methods are what you are seeking for. With your example, ExecuteSqlRaw, e.g. (assuming this is method in your DbContext derived class):
public void ClearIterations()
{
this.Database.ExecuteSqlRaw("ClearIterations");
}
//Interface
public interface IRepository
{
Task AddQualification(int userId, string qualification);
}
Repository class implementing your interface remember to register your interface in your startup class ConfigureServices services.AddScoped<IRepository, Repository>()
public class Repository : IRepository
{
public async Task AddQualification(int userId, string qualification)
{
await appDbContext.Database.ExecuteSqlRawAsync("InsertQualification {0}, {1}", userId, qualification);
}
}
Constructor injection of the interface, then call the method
public async Task OnPost()
{
await _repository.AddQualification(1, "English A-Level");
}
SQL Stored Procedure
CREATE PROCEDURE InsertQualification
-- Add the parameters for the stored procedure here
#UserID int,
#Qualification varchar(255)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.UserQualifications (UserID, Qualification)
VALUES (#UserID, #Qualification)
END
GO

Update integers (or numbers) by difference in EF Core

Does EntityFramework Core provide a way to create an update query in such way, it will create an UPDATE that calculates the change of an numeric column instead of an absolute value?
Context about the question
Consider the following DbContext:
public class MyValue {
public int Id {get;set;}
public int Value {get;set;}
}
public class MyContext : DbContext {
public DbSet<MyValue> Values {get;set;}
}
A simple command could do the following:
var newValue = new MyValue { Value = 1 };
_dbContext.Add(newValue);
_dbContext.SaveChanges();
var value = _dbContext.Values.First();
value = value + 5;
_dbContext.SaveChanges();
The resulting statement that'll be send to the database during SaveChanges()
Will be something like this:
UPDATE SET [Value] = 6 WHERE ID = 1
But I'd like it to formulate an update statement like this:
UPDATE SET [Value] = [Value] + 5 WHERE ID = 1
WHY?
If an update would be created like the second one, it'd not be neccesary for me to take precautions about concurrency. The database should be able to handle that for me.
I am aware, that I could create an StoredProcedure handling the update that way, but that seems not neccesary, if EFCore would just split the Update commands, making (all) numeric columns update by difference.
So - is there an integrated way to do that, or is there a feasible way to implement it during SaveChanges()?

return custom object from stored procedure executed in EntityFramework, can't see object results

I am trying to execute a stored procedure using Entity Framework. I've tried the below and it returns the correct amount of rows, but when I look at the data in the debug window, it just shows my TYPE, it won't let me drill into to see what the actual values are.
SqlParameter param1 = new SqlParameter("#targetDate", filedate);
var result = db.Database.SqlQuery<PositionSheetCompResults>("dbo.comparePositionSheet #targetDate", param1);
Can anyone tell me how to do this?
here are the results it SHOULD return
here is the class
public class PositionSheetCompResults
{
public string AcctNum { get; set; }
You should write ToList<PositionSheetCompResults>();
var result = db.Database.SqlQuery<PositionSheetCompResults>("dbo.comparePositionSheet #targetDate", param1).ToList<PositionSheetCompResults>();

Support for Table Valued Functions in EF6 Code First?

Is it possible to call a TVF in EF6 Code First?
I started a new project using EF6 Database first and EF was able to import a TVF into the model and call it just fine.
But updating the model became very time consuming and problematic with the large read-only db with no RI that I'm stuck dealing with.
So I tried to convert to EF6 code first using the Power Tools Reverse Engineering tool to generate a context and model classes.
Unfortunately the Reverse Engineering tool didn't import the TVFs.
Next I tried to copy the DBFunctions from my old Database First DbContext to the new Code First DbContext, but that gave me an error that my TVF:
"cannot be resolved into a valid type or function".
Is it possible to create a code first Fluent mapping for TVFs?
If not, is there a work-around?
I guess I could use SPs instead of TVFs, but was hoping I could use mostly TVFs to deal with the problematic DB I'm stuck with.
Thanks for any work-around ideas
This is now possible. I created a custom model convention which allows using store functions in CodeFirst in EF6.1. The convention is available on NuGet http://www.nuget.org/packages/EntityFramework.CodeFirstStoreFunctions. Here is the link to the blogpost containing all the details: http://blog.3d-logic.com/2014/04/09/support-for-store-functions-tvfs-and-stored-procs-in-entity-framework-6-1/
[Tested]
using:
Install-Package EntityFramework.CodeFirstStoreFunctions
Declare a class for output result:
public class MyCustomObject
{
[Key]
public int Id { get; set; }
public int Rank { get; set; }
}
Create a method in your DbContext class
[DbFunction("MyContextType", "SearchSomething")]
public virtual IQueryable<MyCustomObject> SearchSomething(string keywords)
{
var keywordsParam = new ObjectParameter("keywords", typeof(string))
{
Value = keywords
};
return (this as IObjectContextAdapter).ObjectContext
.CreateQuery<MyCustomObject>(
"MyContextType.SearchSomething(#keywords)", keywordsParam);
}
Add
public DbSet<MyCustomObject> SearchResults { get; set; }
to your DbContext class
Add in the overriden OnModelCreating method:
modelBuilder.Conventions.Add(new FunctionsConvention<MyContextType>("dbo"));
And now you can call/join with
a table values function like this:
CREATE FUNCTION SearchSomething
(
#keywords nvarchar(4000)
)
RETURNS TABLE
AS
RETURN
(SELECT KEY_TBL.RANK AS Rank, Id
FROM MyTable
LEFT JOIN freetexttable(MyTable , ([MyColumn1],[MyColumn2]), #keywords) AS KEY_TBL
ON MyTable.Id = KEY_TBL.[KEY]
WHERE KEY_TBL.RANK > 0
)
GO
I was able to access TVF with the code below. This works in EF6. The model property names have to match the database column names.
List<MyModel> data =
db.Database.SqlQuery<MyModel>(
"select * from dbo.my_function(#p1, #p2, #p3)",
new SqlParameter("#p1", new System.DateTime(2015,1,1)),
new SqlParameter("#p2", new System.DateTime(2015, 8, 1)),
new SqlParameter("#p3", 12))
.ToList();
I actually started looking into it in EF6.1 and have something that is working on nightly builds. Check this and this out.
I have developed a library for this functionality. You can review my article on
UserTableFunctionCodeFirst.
You can use your function without writing SQL query.
Update
First of all you have to add reference to the above mentioned library and then you have to create parameter class for your function. This class can contain any number and type of parameter
public class TestFunctionParams
{
[CodeFunctionAttributes.FunctionOrder(1)]
[CodeFunctionAttributes.Name("id")]
[CodeFunctionAttributes.ParameterType(System.Data.SqlDbType.Int)]
public int Id { get; set; }
}
Now you have to add following property in your DbContext to call function and map to the property.
[CodeFunctionAttributes.Schema("dbo")] // This is optional as it is set as dbo as default if not provided.
[CodeFunctionAttributes.Name("ufn_MyFunction")] // Name of function in database.
[CodeFunctionAttributes.ReturnTypes(typeof(Customer))]
public TableValueFunction<TestFunctionParams> CustomerFunction { get; set; }
Then you can call your function as below.
using (var db = new DataContext())
{
var funcParams = new TestFunctionParams() { Id = 1 };
var entity = db.CustomerFunction.ExecuteFunction(funcParams).ToList<Customer>();
}
This will call your user defined function and map to the entity.

Custom Initialization Strategy for EF Code First that doesn't drop tables to add a column

The latest EF Code First NuGet package comes with a custom implementation of IDatabaseInitializer called DontDropDbJustCreateTablesIfModelChanged. As the name implies, when a model change is detected, it will not drop and recreate the whole database, just drop and recreate the tables.
Say I have this model class:
public class User
{
public string Username { get; set; }
// This property is new; the model has changed!
public string OpenID { get; set; }
}
How would one go about implementing an IDatabaseInitializer that doesn't drop any tables either. In this case, it would just add an OpenID column to the User table?
I think it is a matter of SQL. So for SQL Server you can write something like:
public class MyInitializer : IDatabaseInitializer<MyContext>
{
public void InitializeDatabase(MyContext context)
{
context.Database.SqlCommand(
#"
IF NOT EXISTS (SELECT 1 FROM sys.columns AS col
INNER JOIN sys.tables AS tab ON tab.object_Id = col.object_Id
WHERE tab.Name = 'User' AND col.Name = 'OpenId')
BEGIN
ALTER TABLE dbo.User ADD OpenId INT;
END");
}
}
But in the same way you can execute such script without adding it to your application which I think is much better approach.
With the current version of Code First, you cannot simply amend your schema and preserve any data that you might have in your tables. If maintaining data, such as reference data / lookup tables is important with this release you can create your own Initializer and override the Seed method to populate your tables
public class MyDbInitializer : DropCreateDatabaseIfModelChanges<MyContext>
{
protected override void Seed(MyContext context)
{
var countries = new List<Country>
{
new Country {Id=1, Name="United Kingdom"},
new Country{Id=2, Name="Ireland"}
};
countries.ForEach(c => context.Countries.Add(c));
}
}
And then use this in your Application_Start:
Database.SetInitializer<MyContext>(new MyDbInitializer());
I believe that this is being addressed currently by the EF Team, but wasn't ready for release at the time the Code First drop came out. You can see a preview here: Code First Migrations