Getting data from stored procedure with Entity Framework - c#

I am trying to get the content a table with a dynamic SQL stored procedure called from the database context object (using Entity Framework 6.1.1), in order to populate a GridView control. I fail to retrieve the data.
Here's the stored procedure. It is for a student demonstration about SQL injection in stored procedures, so I KNOW this is inject-able and it's fine.
ALTER PROCEDURE dbo.SearchProducts
#SearchTerm VARCHAR(max)
AS
BEGIN
DECLARE #query VARCHAR(max)
SET #query = 'SELECT * FROM dbo.Products WHERE Name LIKE ''%' + #SearchTerm + '%'''
EXEC(#query)
END
The C# code behind I then use to execute the stored procedure is :
var db = new MyEntities();
var TEST_SEARCH_TERM = "product";
var result = db.SearchProducts(TEST_SEARCH_TERM);
MyGridView.DataSource = result;
MyGridView.DataBind();
When executed, in the Database Explorer in Visual Studio, the stored procedure works fine. But when executed in the running ASP.NET app, I get an exception in the DataBind() method because result returns -1 instead of an IEnumerable DataSet containing the objects resulting from the stored procedure's SELECT.
How can I retrieve the data and populate my GridView?

Use the following steps to solve this issue:
You need to Import the stored procedure as a Function. Right-click on the workspace area of your Entity model and choose Add -> Function Import.
In the Add Function Import dialog, enter the name you want your stored procedure to be referred to in your model for example Search_Products, choose your procedure from the drop down list, and choose the return value of the procedure to be Entities and choose Products from the drop down list.
Then in the code behind:
var db = new MyEntities();
var TEST_SEARCH_TERM = "product";
var result = db.Search_Products(TEST_SEARCH_TERM);//Search_Products is the name that you specified in Function Import dialog
MyGridView.DataSource = result;
MyGridView.DataBind();
The reason that you get -1 for result is that Entity Framework cannot support Stored Procedure Return values out of the box. I think support of stored procedure return values depends on version of Entity framework. Also Entity Framework doesn't have rich stored procedure support because its an ORM, not a SQL replacement.

I have come across this before with stored procedures using dynamic SQL. I have had success using complex types if I add the line 'SET FMTONLY OFF;' (see https://msdn.microsoft.com/en-us/library/ms173839.aspx) to the top of my stored procedure before it is added to the EF model. Once you have your model setup with your complex type, be sure to remove this line.
Example:
ALTER PROCEDURE dbo.SearchProducts
#SearchTerm VARCHAR(max)
AS
BEGIN
SET FMTONLY OFF;
DECLARE #query VARCHAR(max)
SET #query = 'SELECT * FROM dbo.Products WHERE Name LIKE ''%' + #SearchTerm + '%'''
EXEC(#query)
END

Verify that your EDMX has a return type:
Go to Function Imports --> SearchProducts, and double click it.
In order to utilize a Complex return type, Entity Framework will require that you explicitly define column names in your stored procedure instead of using *.
Once your stored procedure is modified to define the column names, you can update your model in the project. (Note, performing a complete drop of the SP, and then adding it back to your edmx may be the best route.)
EDIT
Maybe you can modify your SP like the following:
ALTER PROCEDURE dbo.SearchProducts
#SearchTerm VARCHAR(max)
AS
BEGIN
SELECT * FROM dbo.Products WHERE Name LIKE '%' + #SearchTerm + '%'
END

You seem to have sorted your problem out, there is official documentation from Microsoft available at the links below:
How to import a stored procedure into your entity data model:
https://msdn.microsoft.com/en-us/library/vstudio/bb896231(v=vs.100).aspx
Complex types in the EF designer:
https://msdn.microsoft.com/en-gb/data/jj680147.aspx
Make sure you are working with the latest version of .net and you keep your model up to date when you make changes to your database.

Related

SQL injection with Entity Framework Database.SqlQuery

In my project I have defined a stored procedure with a example code below:
CREATE PROCEDURE [dbo].[Stored]
#ParameterA AS varchar(128),
#ParameterB AS varchar(128),
#ParameterC AS varchar(400)
AS
BEGIN
DECLARE #query AS Varchar(MAX)
SET #query = 'SELECT *
FROM Table
WHERE A = '''+ #ParameterA + ''
IF #ParameterB = 'B'
BEGIN
SET #query = #query + ' AND C=''' + #ParameterC + ''
END
EXECUTE sp_executesql #query
END
I call this procedure with Entity Framework through the following code:
DBContext.Database.SqlQuery<Object>("Stored",
new SqlParameter("#p0", Param0),
new SqlParameter("#p1", Param1),
new SqlParameter("#p2", Param2)).ToList();
If I call a stored procedure with the string below, I generate a SQL injection:
Param2 = "ABC' ; DROP TABLE Table2"
How can I prevent this with Entity Framework?
You cannot
The underlying SQL procedure is faulty and a security nightmare. There is no way you can repair that on the layer on top of it. You are doing the best you can in EntityFramework, but it's still unsafe. You need to repair the problem (SQL proc) and not apply band aids to the layer using it.
sp_executesql seems to be a good starting point for a procedure that needs to have dynamic SQL and bind parameters.
you are creating a dynamic query, where you are concatenating parameters. this is causing issue.
do not use dynamic query, or validate parameters (if it contains any keywords or characters)
you can also rewrite your query into IF-ELSE structure on basis of parameters, so you do not need dynamic query.

Stored procedure cannot create complex type in entity framework

I have added this store procedure through phpmyadmin:
CREATE DEFINER=`root`#`localhost` PROCEDURE `SelectUser`()
LANGUAGE SQL
NOT DETERMINISTIC
NO SQL
SQL SECURITY DEFINER
COMMENT ''
Select * from User
In my edmx but when create a complex type and do the Get column information that gives
The store procedure returns no columns
try add
SET FMTONLY OFFin your sql statement

Passing an array of objects to SQL stored procedure

I have 2 tables
payment (payment_id, otherCosts, GarageCosts)
spareparts (payment_id, sparepartId, sparePartQty)
In payment table payment_id is autogenerated. Apart from otherCosts and garagecosts values, in my C# asp.net application there is an array of objects with
{ sparepartId : 'Somevalue', sparePartQty : 'somevalue' }
What I need to do is in a stored procedure first enter the record into payment table with garage costs and others costs value. Then return the last generated payment ID and enter it to spareParts table as paymentId for each of the value pairs in the array.
What is the way to achieve this? Please help.
Based on your tags I am going to assume we are talking about SQL Server / T-SQL.
You could do all this in one stored procedure:
CREATE PROCEDURE dbo.Foo ... /* input parameters */
AS
BEGIN
DECLARE #PaymentId int
INSERT INTO payment(otherCosts, GarageCosts) VALUES (...)
SET #PaymentId = SCOPE_IDENTITY()
INSERT INTO spareparts(payment_id, sparepartId, sparePartQty) VALUES(#PaymentId, ...)
END
GO
You may want to also look into ##IDENTITY but make sure you read about ##IDENTITY and SCOPE_IDENTITY and understand the risks associated with the first one.
If you need to have two separate sprocs you can do that too and here is how the first sproc would look like. Note that the #PaymentId is an output parameter which means that the caller can retrieve it and pass it to the second procedure.
CREATE PROCEDURE dbo.Foo
/* input parameters */
#PaymentId int OUT
AS
BEGIN
INSERT INTO payment(otherCosts, GarageCosts) VALUES (...)
SET #PaymentId = SCOPE_IDENTITY()
END
GO
Edit - after the scope of the question was clarified:
If you need to call the second stored procedure and pass it an array of parameters, with SQL Server 2008 or newer you can use TVP (Table Value Parameters). To see how you can use them in stored procedures and how you can pass them from C# code see Table Value Parameters in SQL Server 2008 and .NET (C#) or Table-Valued Parameters.
You can also use TVPs with the solution where you only have one sproc.
To solve your problem try this
First insert your data in payment table with otherCosts and GarageCosts.
Then create a procedure to get the latest stored payment_id from payment table
create procedure select_last_payment_id
as
begin
select top 1 payment_id
from payment
order by payment_id desc
end
Lastly get that payment_id by running stored procedure and assigning it to payment_id of spareparts table and storing spareparts data.
Hope it works for you.

Error: Unknown Return Type, The return type for the following stored procedures could not be detected

I am using the following technologies:
MS SQL 2008 R2
Visual Studio 2010
Silverlight project
LINQ to SQL
When dragging a stored procedure into the data model, and the stored procedure returns an OUTPUT parameter, it works fine. However when the stored procedure returns a result set created with dynamic SQL I receive the following error:
"Unknown Return Type, The return type for the following stored procedure could not be detected."
If the stored procedure does not use dynamic SQL, it works fine.
(For example: SELECT column1 from table2)
The stored procedure however uses dynamic SQL to query a specified view for the data. Simplified below for illustration purposes:
CREATE PROCEDURE GetViewData
#ViewName nvarchar(150)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql nvarchar(MAX)
SET #Sql = 'SELECT Column1, Column2, Column3 FROM' + #ViewName
EXEC sp_executesql #Sql
END
Instead of returning the result set directly, I can place the result set into a temp table. This however gives the same error.
According to the article from Ritesh, when placing the result set into a table type variable this should do the trick, however this is not possible in dynamic sql as the scope is only within the executed dynamic sql.
Ritesh's article:
http://riteshkk2000.blogspot.com/2010/08/error-unknown-return-type-return-types.html
Upon further investigation I realized that the meta data received by LINQ to SQL does not contain the necessary information to define the type.
Apparently with SQL 2012 this can be resolved by using "WITH RESULTS SET" to actually define the meta data manually.
I resolved this by creating another stored procedure, calling GetViewData without using dynamic SQL and populating a table type variable:
CREATE PROCEDURE CallGetViewData
#ViewName nvarchar(150)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #ViewResultSet TABLE
(
column1 nvarchar(50),
column2 int,
column3 float
)
INSERT INTO #ViewResultSet
EXEC GetViewData #ViewName
SELECT column1, column2, column3 FROM #ViewResultSet
END
Now when I drag stored procedure CallGetViewData into the data model, it detects the return type correctly.
One trick you can try is to manually create a model that represents the shape of the object that the Stored Proc will return. Then using the designer, don't drag the proc into the method pane, but rather drop it on the type you created manually. LINQ to SQL will then use that class as the result type.
If you already have the method defined for the stored proc, you can change the Return Type using the F4 property window to any type that matches the result shape that you've defined in the model.

Adding stored procedures complex types in Entity Framework

I am trying to use a stored procedure in Entity Framework that returns nothing.
I did the following:
Added a function (right click on stored procedure -> add -> function import-> Complex Type -> Get column information -> create New Complex-Type)
My function name: summarySP_Result. After building the project the entity class is not generated in Generated_code (BusinessAccount.web.g.cs)
But entity classes for tables and views are all created but nor for stored procedure.
Can anybody give the idea why it is not generated entity class in BusinessAccount.web.g.cs?
Update :
Let me confirm ReturnDataFromTemTable_result entity class created in your XXXXXX.web.g.cs class.
Like :
[DataContract(Namespace="http://schemas.datacontract.org/2004/07/BizFramework.Web.Model")]
public sealed partial class ReturnDataFromTemTable_Result : Entity
{
-------------------
}
OK - here's the step-by-step way of doing this:
(1) add your stored procedure to the EDMX file (when you first create it, or later on by using Update model from database and picking that stored procedure)
(2) once you have the stored procedure in your model - use the Model Browser to add a Function Import :
(3) the next dialog that pops up is vitally important - you need to (1) define that the stored procedure returns a collection of complex types, then you need to (2) get the column info from that stored procedure to know what columns it will return, then (3) you tell Visual Studio to generate a new complex type based on that column info:
(4) once you've done that - you should now see the stored procedure in your conceptual model section in the Model Browser, and the newly generated complex type should show up there, too:
This is for Ross Brigoli
Try adding this line to the beginning of your stored procedure:
SET FMTONLY OFF
You can remove this after you have finished importing.
Source:-
Why can't Entity Framework see my Stored Procedure's column information?
If this is still unresolved, after you Add the Function Import, go to the Solution Explorer, right click your {name}.Context.tt file and do "Run Custom Tool". The method will now show up in your derived Context class.
This seems like a bug in Visual Studio 2012, which is what I am using, I haven't applied Update 1, I will try to see if that fixes it.
As Sandeep said,
EF doesn't support importing stored procedures which build result set from Dynamic queries or Temporary tables.
But you don't have to rewrite your whole SP.
Just write another one, with the same name, that returns the correct row format without using dynamic SQL or a temp table. Then use the EF SP adding function, which will now auto generate the complex type.
Edit: It's actually easier to make a comment at the top of the SP that immediately selects the desired row with all the data types specified with CASTS. When you need to import the SP into EF, just uncomment the code.
e.g.
CREATE PROCEDURE myProc ()
AS
BEGIN
-- uncomment the following row to import:
-- SELECT CAST( 0 AS int) AS column1Name, CAST( 'a' AS varchar(50)) AS clumn2name
-- comment out the SP content when you want to import it.
< proper SP content >
END
Then drop the stored proc and create the original.
Save this temporary importing SP you have made in case you need it again, though.
EF doesn't support importing stored procedures which build result set from:
Dynamic queries
Temporary tables
Re-write your stored procedure to use a table variable instead.
remember to drop the stored procudure and function import from your model before updating as it wont generate the complex type unless it also adds the stored procedure. or go to function import properties and use the get column information feature after updating your stored procedure.
create procedure [dbo].[usp_InsertOrUpdate]
/*if your table(tbl_InsertOrUpdate) as 3 columns like uniqueid,col1,col2*/
#uniqueId bigint NULL,/*if insert send value as null or 0*/
#col1 bigint null,
#col2 [varchar](500) NULL
as
begin
set nocount ON
SET FMTONLY OFF
/* for giving result which column updated(uniqueId) and is it insert or update(IsInsert)*/
declare #varResult table (uniqueId bigint ,IsInsert bit )
/*create a var table before inserting original table*/
declare #varInsertOrUpdate table (
uniqueId bigint ,
col1 [bigint] ,
col2 [varchar]
)
/*default we are feel as update only*/
insert into #varResult (uniqueId,IsInsert) values (#uniqueId,0)
/*insert into var table*/
INSERT INTO #varInsertOrUpdate (uniqueId,col1,col2)
VALUES
(#uniqueId,#col1,#col2)
/*Insert into original table with where condition without if else*/
INSERT INTO tbl_InsertOrUpdate (col1,col2)
select col1,col2 from #varInsertOrUpdate
where uniqueId!=0;
/*if its insert updating result returning table*/
update #varResult set
uniqueId=IDENT_CURRENT('tbl_InsertOrUpdate'),
IsInsert=1 where #uniqueId=0;
/*updating table where #uniqueid is null or empty*/
UPDATE tbl_InsertOrUpdate
SET col1=#col1,
col2=#col2,
WHERE uniqueId=#uniqueId and #uniqueId!=0
select * from #varResult
end
To add complex type correctly, go Model browser, right click on function, then display edit, click edit an fill the dialog box. The name of the function should be same as name of the stored procedure. Click OK button. Now function is created. Then right click on the created function and go edit again.There is a update button aside of complex type button.Update it using that update button. Now complex type is created completely.
This is my SP to Implement the multiple search
***************************************************
CREATE PROCEDURE [dbo].[uspSEARCH_POSITIONS]
#OBJ_TYPE_REQUEST varchar(2000),--'FIRST_NAME;SEARCHVALUE|LAST_NAME;SEARCHVALUE|JOB_DESCRIPTION;SEARCHVALUE'
#DELIMITER varchar(10) --'| Which seperates the col;searchvalue|col;searchvalue
AS
BEGIN
SET FMTONLY OFF
DECLARE
#lLastName varchar(100),
#lFirstName varchar(100),
#lPositionNumber varchar(20),
#lJobDescription varchar(50),
#lJobCode varchar(20),
#lOccupancyIndicator varchar(50),
#ldeleimitercolsearchval varchar(10)
SET #ldeleimitercolsearchval =';'
CREATE TABLE #TempTable (ColSearchValues VARCHAR(2000))
INSERT INTO #TempTable
SELECT * FROM [dbo].[fnSplit](#OBJ_TYPE_REQUEST,#DELIMITER)--'fname;searchvalfname|lname;searchvallname|jobcode;searchvaljobcode','|')
SELECT #lLastName=SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%last%'
SELECT #lFirstName =SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%first%'
SELECT #lPositionNumber =SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%position%'
SELECT #lJobDescription=SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%jobd%'
SELECT #lJobCode=SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%jobc%'
SELECT #lOccupancyIndicator=SUBSTRING(ColSearchValues,CHARINDEX(#ldeleimitercolsearchval ,ColSearchValues)+1,LEN(ColSearchValues)) from #TempTable where lower(ColSearchValues) like '%ccupancy%'
SELECT [PS].[POSITION_NUMBER]
,[PS].[COST_CENTER]
,[PS].[JOB_CODE]
,[PS].[JOB_CODE_DESCRIPTION]
,[PS].[SITE_CODE]
,[EMP].[EMPLOYEE_ID]
,[EMP].[EIN]
,[EMP].[GRADE]
,[EMP].[LOGIN_ID]
,[EMP].[FIRST_NAME]
,[EMP].[LAST_NAME]
,LTRIM(RTRIM(ISNULL([EMP].[LAST_NAME],''))) + ',' +LTRIM(RTRIM(ISNULL([EMP].[FIRST_NAME],''))) AS [FULL_NAME]
,[EMP].[DISTRICT]
,[EMP].[SUPERVISOR_EIN]
,COUNT(*) OVER() AS TOTAL_RECORD_COUNT
FROM [DBSERVER].[dbo].[uvwPOSITION_SEARCH] PS
LEFT JOIN [DBSERVER].[dbo].[uvwEMPLOYEES] EMP
ON PS.POSITION_NUMBER=EMP.POSITION_NUMBER
WHERE
(#lLastName IS NULL OR [LAST_NAME] LIKE '%' + #lLastName + '%')
AND (#lFirstName IS NULL OR [FIRST_NAME] LIKE '%' + #lFirstName + '%')
AND (#lPositionNumber IS NULL OR [PS].[POSITION_NUMBER] LIKE '%' + #lPositionNumber + '%')
AND (#lJobDescription IS NULL OR [PS].[JOB_CODE_DESCRIPTION] LIKE '%' + #lJobDescription + '%')
AND (#lJobCode IS NULL OR [PS].[JOB_CODE] LIKE '%' + #lJobCode + '%')
AND (#lOccupancyIndicator IS NULL OR [EMP].[FILLED_VACANT] LIKE '%' + #lOccupancyIndicator + '%')
END
Now you can consume above SP in edmx using below
Adding stored procedures complex types in Entity Framework
Why can't Entity Framework see my Stored Procedure's column information?
And in case you have to update your SP below worked for me.
Updating Complex Type if Stored Procedure Updates
How Do I Get Entity Framework To Update Complex Types?
For me, Im having problems where importing my Stored Procedure into EF is not generating the Complex Entity return object (automatically). I found however, after commenting out sections of my sproc (aka stored procedure), that when I then re-imported the stored procedure back in (ie refreshed using the Get Column Information button in the Function Import Edit screen), that the Complex type could then be generated!
In short, there could be a where clause (or maybe something else) causing EF to not generate the Complex Type. Try commenting out sections of your sproc and re-importing the sproc to
UPDATE:
Further to my investigation above, I found that the reason the Complex Entity was not being generated was because my sproc was using a view (instead of a typical table). For curiosity sake, I changed the view to another table just to see what would happen, and the complex entity generated.
So, in short, it looks like Complex Entities might not generate automatically if you have a view. To try, I ripped out the view temporarily, re-import the sproc, generated the Complex Entity, then put the view back in. But now my code gives exceptions.
Will update on this later when I learn more =)
UPDATE:
Fixed the issue. Really silly mistake! The viewname that I was using was not spelled right =D. Im sort of angry that an error wasnt thrown by Sql Server when I created the sproc..... I guess that is life :) Alas, problem now fixed!
The issue of complex type not appearing may happen due to a different reason as well which is what I faced in our case. The issue was due to a syntax error in the SPROC where temp table was defined as below -
create table #temp(
col1 int,
col2 nvarchar(100),
col3 nvarchar(100), -- Notice the comma in the end
);
Surprisingly, SQL Server doesn't throw any error when you compile the sproc. Removing the comma fixed the problem for us.
In short, while some of the above solutions might work depending on the specific issue, my suggestion is to check your sproc for such syntactical errors that SQL might ignore but could be the underlying reason for this problem. Thanks.
Go to the Model Browser
If you need to modify existing function
Under the Function Imports >> Select the function to be modified >> Click Edit
You will need to update the function to refresh and you can see the columns need to be added

Categories