After trying to work this out for a few hours, I'm beginning to think I can't solve this without your help. Situation:
My stored procudure in SQL Server Management Studio (basically):
SET NOCOUNT ON;
DELETE myTable
... -- Complex join query that deletes duplicates
RETURN ##ROWCOUNT -- The number of rows affected
This procedure works well, if I run it in SSMS the following query is run:
USE myDb
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[StoredProcedure]
#var1 = N'asd',
#var2 = N'fgh',
#var3 = N'jkl'
SELECT 'Return Value' = #return_value
GO
The result is shown as 1 row with name Return Value and value 8700 (this is correct, it's the number of rows that was deleted by the query).
My problems begin in my C#/NHibernate code. In my NHibernate mapping:
<sql-query name="MyStoredProcedureName">
exec dbo.StoredProcedure #var1=:var1, #var2=:var2, #var3=:var3
</sql-query>
In my method where I call the stored procedure:
var deletedEntities = session.GetNamedQuery("MyStoredProcedureName")
.SetString("var1", "asd")
.SetString("var2", "fgh")
.SetString("var3", "jkl")
.ExecuteUpdate();
This results in deletedEntities = -1. I would like it to be deletedEntities = 8700.
I'd like to point out that the following did return the number of affected rows:
var deletedEntities = session.CreateQuery("DELETE Class c WHERE c.var1 = :var1 AND c.var2 = :var2 AND c.var3 = :var3")
.SetString("var1", var1)
.SetString("var2", var2)
.SetString("var3", var3)
.ExecuteUpdate();
Where Class is the class that belongs to the NHibernate mapping.
So, to summarize:
ExecuteUpdate on a SIMPLE session.CreateQuery gives me back the number of affected rows (GOOD)
ExecuteUpdate on a COMPLEX session.GetNamedQuery gives me back -1 (BAD)
This same complex stored procedure gives me back the desired 8700 when I execute it in SSMS (GOOD)
Anyone has an idea of how to fix 2?
Using http://csharptechies.blogspot.co.uk/2013/04/how-to-get-return-value-from-stored.html I got my code working. In the procedure, replaced RETURN ##ROWCOUNT with SELECT ##ROWCOUNT
In the C# code, I replaced .ExecuteUpdate() with .UniqueResult(). UniqueResult returns an object, which will contain the ##ROWCOUNT value (need to convert to int as stated in the link above).
So the solution is a combination (I'd like to use the mapping):
Use SELECT instead of RETURN
Use .UniqueResult() instead of .ExecuteUpdate()
Edit: I got another problem, the query had a timeout whenever there was a large number of records to be removed. I fixed this with the help of other questions, such as SQL Server: Effects of using 'WITH RECOMPILE' in proc definition?.
Use WITH RECOMPILE in a complex query on large tables (my query involved a left outer join, min(id) and group by's, the number of records to be affected are often around 20.000 but can run up to 100.000+ and my table has over 350.000.000 records in it) to avoid timeouts.
I have looked for a few days for a solution but can't find one.
I am creating a stored procedure to search a table using fulltext search. I will then combine the result from 15 of these stored procedures into a list ordered by their ranking.
Here is the code for the stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SpAdvancedSearchICAPUsers]
#searching nvarchar(100) = ''
AS
BEGIN
SET NOCOUNT ON;
Set FMTONLY OFF;
DECLARE #SQLQuery AS NVarchar(max)
IF #searching IS NULL OR #searching = ''
BEGIN
SET #SQLQuery = 'SELECT (all field names listed separated by commas) FROM Users'
END
ELSE
BEGIN
SET #SQLQuery = 'SELECT (all field names listed separated by commas), fts.Rank FROM Users as p JOIN
FREETEXTTABLE(Users, (fields to search for listed separated by commas), #searching)
as fts on p.userID = fts.[KEY] WHERE 1=1'
END
EXEC #SQLQuery
END
I did approached this project doing Model first. I added my stored procedures to my model by right clicking and pressing: Add New > Function Import...
I set the name of the function import, selected the stored procedure, selected the "Returns a Collection Of" to Entities: (desired data type the SP returns, in this case, Users).
When I use the stored procedure like this:
newSearch.Users = db.SpAdvancedSearchICAPUsers(search); //search is a string
newSearch.Users is an IENumerable<Users>. It says the return type is an int for the stored procedure. I get the following error:
Cannot implicitly convert type 'int' to 'System.Collections.Generic.IEnumerable;
I have also tried adding a parameter definition variable as such right below declaring #SQLQuery like this:
Declare #ParamDefinition As NVarchar(4000)
and then I set it and try and run it like this:
Set #ParamDefinition = '#searchingnvarchar(100)'
Exec sp_executesql #SQLQuery, #ParamDefinition, #searching
#searching is the string which is passed in to search.
I have even tried dragging all of my tables into a DBML file, because I've used stored procedures this way successfully before. When I dragged in the SP to the table I get this message:
UPDATE: Eddie in the comments specified that in the DBML after your drag in the stored procedure, you can set the return type (I chose the User object type). This works. I would rather not do this though. I'll have it as a temporary solution but I have 15 stored procedures I'd have to do that to.
Does anyone know how to get the correct output from the stored procedure using the EDMX file instead of a DBML? When I look at the properties after right clicking in the EDMX and selection "Mapping Details > Stored Procedures / Functions". The return type in the EDMX doesn't have anything. It has no drop downlist, nothing.
I'm not sure if I'm hitting the spot here, but if you are doing something like newSearch.Users = db.SpAdvancedSearchICAPUsers(search);
and newSearch.Users is of type User Entity, then perhaps writing the search statement as
Set #SQLQuery = 'SELECT p.* FROM Users as p JOIN
FREETEXTTABLE(Users, (fields to search for listed separated by commas), #searching)
as fts on p.userID = fts.[KEY] WHERE 1=1
ORDER BY fts.[Rank]'
would work for the simple fact that it is returning the fields a User Entity expects (i.e.: no rank field).
To better diagnose whether this is the case or not, the question turns to:
Does the same problem happen when you send an empty search field?
i.e.:
newSearch.Users = db.SpAdvancedSearchICAPUsers('');
If it works for the latter case, then perhaps the aforementioned change would do.
Please let me know.
I have a stored procedure that contains dynamic select. Something like this:
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--#p1 int = 0,
--#p2 int = 0
#groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE #query NVARCHAR(max)
SET #query = 'SELECT * FROM CUSTOMERS WHERE Id = ' + #groupId
/* This actually contains a dynamic pivot select statement */
EXECUTE(#query);
END
In SSMS the stored procedure runs fine and shows result set.
In C# using Entity Framework it shows returning an int instead of IEnumerable?
private void LoadTestRecords()
{
TestRecordsDBEntities dataContext = new TestRecordsDBEntities();
string id = ddlGroupId.SelectedValue;
List<TestRecord> list = dataContext.usp_GetTestRecords(id); //This part doesn't work returns int
GridView1.DataSource = list;
}
Generated function for usp_GetTestRecords
public virtual int usp_GetTestRecords(string groupId)
{
var groupIdParameter = groupId != null ?
new ObjectParameter("groupId", groupId) :
new ObjectParameter("groupId", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("usp_GetTestRecords", groupIdParameter);
}
I get this when I have a stored procedure that includes an "exec" call into a temporary table, such as:
insert into #codes (Code, ActionCodes, Description)
exec TreatmentCodes_sps 0
It appears that Entity Framework gets confused as to what should be returned by the procedure. The solution I've come across is to add this at the top of the sproc:
SET FMTONLY OFF
After this, all is well.
I got the same problem, and found solution here
Move to your .edmx
At Model Browser Window/Function Imports find your procedure then double click it
Change the return type to you want
Save .edmx and check the return type again.
It should be what you need now.
Entity Framework can't tell what your stored procedure is returning. I've had success creating a table variable that mirrors the data from your SELECT statement. Just insert into the table variable then do a select from that table variable. EF should pick it up.
See Ladislav Mrnka's answer in this Stack Overflow post
https://stackoverflow.com/a/7131344/4318324
I had the same basic problem.
Adding
SET FMTONLY OFF
To a procedure you are trying to import during the import will address this problem.
It's a good practice to remove the line afterwards unless the purpose of the database is solely to provide schema for EF (Entity Framework).
The main reason for caution is that EF uses this setting to prevent data mutations when trying to obtain metadata.
If you refresh your entity model from a database any procedures with this line in them can potentially update the data in that database just by trying to obtain the schema.
I wanted to add a further note on this so it's not needed to fully scan through the other link.
if you want to try to use FMTONLY here are a couple things to keep in mind.
when FMTONLY is on:
1) only the schema is returned (no) rows.
similar to adding a blanket false statement to your where clause (ie "where 1=0")
2) flow control statements are ignored
Example
set fmtonly on
if 1=1
begin
select 1 a
end
else
begin
select 1 a,2 b
end
while 1=1
select 1 c
The above returns NO rows whatsoever and the metadata for each of the three queries
For this reason some people suggest toggling it off in a way that takes advantage of it's non-observance of flow control
if 1=0
begin
set fmtonly off
end
In fact you could use this to introduce logic that tracks this
set fmtonly off
declare #g varchar(30)
set #g = 'fmtonly was set to off'
if 1=0
begin
set fmtonly off
set #g = 'fmtonly was set to on'
end
select #g
Think VERY CAREFULLY before trying to use this feature as it is both deprecated and potentially makes sql extremely hard to follow
the MAIN concepts that need to be understood are the following
1. EF turns FMTONLY on to prevent MUTATING data from executing stored procedures
when it executes them during a model update.
(from which it follows)
2. setting FMTONLY off in any procedure that EF will attempt to do a schema scan
(potentially ANY and EACHONE) introduces the potential to mutate database
data whenever *anyone* attempts to update their database model.
Entity Framework will automatically return a scalar value if your stored procedure doesn't have a primary key in your result set. Thus, you'd have to include a primary key column in your select statement, or create a temp table with a primary key in order for Entity Framework to return a result set for your stored procedure.
I had the same problem, I changed the name of return fields by 'AS' keyword and addressed my problem. One reason for this problem is naming column names with SQL Server reserved keywords.
The example is fallows:
ALTER PROCEDURE [dbo].[usp_GetProducts]
AS
BEGIN
SET NOCOUNT ON;
SELECT
, p.Id
, p.Title
, p.Description AS 'Description'
FROM dbo.Products AS p
END
Best solution I found is to cheat a little.
In the store procedure, comment everything, put a first line with a select [foo]='', [bar]='' etc...
Now update the model, go to the mapped function, select complex type and click on Get Column Information and then Create Complex Type.
Now comment the fake select and un-comment the real store procedure body.
When you generated your model class for your stored procedure, you chose scalar return result by mistake. you should remove your stored procedure from your entity model, then re-add the stored procedure. In the dialog for the stored procedure, you can choose the return type you are expecting. Do not just edit the generated code.. this may work now, but the generated code can be replaced if you make other changes to your model.
I have pondered this a bit and I think I have a better/simpler answer
If you have a complex stored that gives entity framework some difficultly (for current versions of Entity Framework that are using the FMTONLY tag to aquire schema)
consider doing the folowing at the beginning of your stored procedure.
--where [columnlist] matches the schema you want EF to pick up for your stored procedure
if 1=0
begin
select
[columnlist]
from [table list and joins]
where 1=0
end
if you are okay loading your result set into a table variable
you can do the following to help keep your schema in sync
declare #tablevar as table
(
blah int
,moreblah varchar(20)
)
if 1=0
begin
select * from #tablevar
end
...
-- load data into #tablevar
select * from #tablevar
If you need to do this, then you might be better off just making a partial of the dbcontext and creating the C# function yourself that will use SqlQuery to return the data you need. Advantages over some of the other options is:
Don't have to change anything when the model updates
Won't get overwritten if you do it directly in the generated class (someone above mention this as if it's an option :) )
Don't have to add anything to the proc itself that could have side effects now or later on
Example Code:
public partial class myEntities
{
public List<MyClass> usp_GetTestRecords(int _p1, int _p2, string _groupId)
{
// fill out params
SqlParameter p1 = new SqlParameter("#p1", _p1);
...
obj[] parameters = new object[] { p1, p2, groupId };
// call the proc
return this.Database.SqlQuery<MyClass>(#"EXECUTE usp_GetTestRecords #p1, #p2, #groupId", parameters).ToList();
}
}
Just change to
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--#p1 int = 0,
--#p2 int = 0
#groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM CUSTOMERS WHERE Id = #groupId
END
I know this is an old thread but in case someone has the same problems I'll tell my woes.
As a help to find the issue, run sql profiler when you add your stored proc. Then you can see what entity framework is passing as parameters to generate your resultset. I imagine nearly always it will pass null parameter values. If you are generating sql on the fly by concatenating string values and parameter values and some are null then the sql will break and you wont get a return set.
I haven't needed to generate temp tables or anything just an exec command.
Hope it helps
During import
SET FMTONLY ON
can be used for taking the sp schema.
If you change the sp and want to update the new one, you should delete the old defined function from edmx file (from xml), because although deleting sp from model browser, it is not deleted in edmx. For example;
<FunctionImport Name="GetInvoiceByNumber" ReturnType="Collection(Model.Invoice_Result)">
<Parameter Name="InvoiceNumber" Mode="In" Type="Int32" />
</FunctionImport>
I had the same problem, and when I delete the FuctionImport tag of corresponding sp totally, the model updated right. You can find the tag by searching the function name from visual studio.
You may have luck opening up the model browser, then going to Function Imports, double clicking the stored procedure in question and then manually clicking "Get Column Information" and then clicking "Create New Complex Type". This usually sorts out the problem.
Well I had this issue as well but after hours of online searching none of above methods helped.
Finally I got to know that It will happen if your store procedure is getting some parameters as null and which generate any error in query execution.
Entity Framework will generate method for store procedure by defining the complex entity model. Due to that null value your store procedure will return and int value.
Please check your store procedure either its providing empty result set with null values. It will fix your problem. Hopefully.
I think this is a problem of permissions on the database, I don't know what exactly could be, but, in my job we use Active Directory users to grant applications connect to databases, this accounts are specially created for the applications, each app has its own user account, well, as a developers I have permissions for read, write and other basic things, no alter, and no advanced features, and I have this same problem running Visual Studio with my normal account, then, what I did was to open Visual Studio selecting the option "as a different user" on the context menu, and I put the AD login granted for the application and voila!, now my Stored Procedures are loading with all the fields I was expected, before that, my Stored Procedures was returning as int. I hope this help someone, maybe the VIEW DEFINITION permissions on database account do the trick
If SQL Authentication is in place, verify that the user credential that is being used to connect Entity Framework to the database has the proper rights to read from CUSTOMERS table.
When Entity Framework uses SQL Authentication to map complex objects (i.e stored procedures that SELECTs more than one column), if any of the tables from within such stored procedure don't have set up the Read permission, the mapping will result in returning INT instead of the desired Result set.
Here is my current implementation of a stored procedure which returns Order status for a given Order ID. There are two situations,
there is matched Order ID and I will retrieve the related status,
there is no matched Order ID (i.e. non-existing Order ID).
My confusion is, how to implement the two functions elegantly/efficiently in one stored procedure, so that I return matched Order ID for situation 1 and also indicate client no matched Order ID in situation 2?
I am using VSTS 2008 + C# + ADO.Net + .Net 3.5 as client, and using SQL Server 2008 as server.
CREATE PROCEDURE [dbo].[GetStatus]
#ID [nvarchar](256),
#Status [int] output
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT #Status = [Status]
FROM [dbo].[OrderStatus]
WHERE (#ID = [ID]);
END
thanks in advance,
George
why are you using output parameter.
you just need to take your stored procedure result in dataset of the data access layer.
just check that if (dataset != null) then take value else return appropriate message to your business layer.
There are multiple approaches you can take:
Keep everything as is and in your .NET code, if the #status value returned is DBNull, then it will indicate situation 2, otherwise situation 1.
Add a RETURN statement to the SPROC and use
Dim returnValue As New SqlParameter("#RETURN_VALUE", SqlDbType.Int)
returnValue.Direction = ParameterDirection.ReturnValue
Cmd.Parameters.Add(returnValue)
in your .NET code to explicitly identify what the SPROC returned and take action accordingly.
As an additional tip, use a SET instead of SELECT when assigning the value to #Status variable in the SPROC. This will guarantee that you get a NULL back if there is no match found. So,
`
-- Insert statements for procedure here
SET #Status = SELECT [Status]
FROM [dbo].[OrderStatus]
WHERE (#ID = [ID]);`
You can use the "if statements" inside the stored procedures. the web site at bottom gives you some tips.
http://translate.google.com.br/translate?u=http%3A%2F%2Fmail.firebase.com.br%2Fpipermail%2Flista_firebase.com.br%2F2005-November%2F021883.html&sl=pt&tl=en&hl=pt-BR&ie=UTF-8
I use the following stored procedure from my SQL Server 2008 database to return a value to my C#-Program
ALTER PROCEDURE [dbo].[getArticleBelongsToCatsCount]
#id int
AS
BEGIN
SET NOCOUNT ON;
DECLARE #result int;
set #result = (SELECT COUNT(*) FROM art_in_cat WHERE child_id = #id);
return #result;
END
I use a SQLCommand-Object to call this Stored Procedure
public int ExecuteNonQuery()
{
try
{
return _command.ExecuteNonQuery();
}
catch (Exception e)
{
Logger.instance.ErrorRoutine(e, "Text: " + _command.CommandText);
return -1;
}
}
Till recently, everything works fine. All of a sudden, the stored procedure returned -1. At first, I suspected, that the ExecuteNonQuery-Command would have caused and Exception, but when stepping through the function, it shows that no Exception is thrown and the return value comes directly from return _command.ExecuteNonQuery();
I checked following parameters and they were as expected:
- Connection object was set to the correct database with correct access values
- the parameter for the SP was there and contained the right type, direction and value
Then I checked the SP via SQLManager, I used the same value for the parameter like the one for which my C# brings -1 as result (btw. I checked some more parameter values in my C' program and they ALL returned -1) but in the manager, the SP returns the correct value.
It looks like the call from my C# prog is somehow bugged, but as I don't get any error (it's just the -1 from the SP), I have no idea, where to look for a solution.
ExecuteNonQuery will return the number of rows affected by an INSERT, UPDATE or DELETE operation, not the return value of a stored procedure. If it was working in the past, is there a possibility that data was being modified by your query?
To get the return value of a stored procedure, add a parameter to your command with ParameterDirection.ReturnValue. After calling ExecuteNonQuery(), that parameter will be populated with the return value of the stored procedure.
return _command.ExecuteNonQuery();
should be
return _command.ExecuteScalar();
Reason being that it is a query you are running, a query that returns a single value. ExecuteNonQuery() assumes there is nothing being returned at all (such as running an UPDATE query).
It looks like you are returning the status of the Count query execution when you really want the value of COUNT (*).
I think it should be like this:
ALTER PROCEDURE [dbo].[getArticleBelongsToCatsCount]
#id int,
#NumberOfRows int OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT #NumberOfRows = COUNT(*) FROM art_in_cat WHERE child_id = #id;
END
From MSDN ExecuteNonQuery:
You can use the ExecuteNonQuery to
perform catalog operations (for
example, querying the structure of a
database or creating database objects
such as tables), or to change the data
in a database without using a DataSet
by executing UPDATE, INSERT, or DELETE
statements.
Although the ExecuteNonQuery returns
no rows, any output parameters or
return values mapped to parameters are
populated with data.
For UPDATE, INSERT, and DELETE
statements, the return value is the
number of rows affected by the
command. When a trigger exists on a
table being inserted or updated, the
return value includes the number of
rows affected by both the insert or
update operation and the number of
rows affected by the trigger or
triggers. For all other types of
statements, the return value is -1. If
a rollback occurs, the return value is
also -1.
ExecuteScalar is probably what you want.
Your SP could then be this:
ALTER PROCEDURE [dbo].[getArticleBelongsToCatsCount]
#id int
AS
BEGIN
SET NOCOUNT ON;
SELECT COUNT(*) FROM art_in_cat WHERE child_id = #id
END
ExecuteNonQuery returns a value of -1 for commands that don't affect any rows. See the documentation at MSDN. You need to check the value of the output parameter #result.