Add stored procedure to Entity Framework [duplicate] - c#

This question already has answers here:
EF4 - The selected stored procedure returns no columns
(16 answers)
Closed 6 years ago.
I have a stored procedure which generate itself dynamically, in detail I am adding to where, order by clauses dynamically.
Here is my stored procedure:
ALTER PROCEDURE [dbo].[sp_GetConsultants]
#SearchQuery VARCHAR(MAX) = NULL,
#SortDataField VARCHAR(100),
#SortOrder VARCHAR(4),
#PageNum INT,
#PageSize INT,
#sql NVARCHAR(MAX) = NULL OUTPUT
AS
BEGIN
SET #sql = N'
WITH cte AS
(
SELECT
ID, [NO], Firstname, Lastname, ReferanceID,
CAST('''' AS VARCHAR(MAX)) AS ReferanceNO
FROM
dbo.Consultants
WHERE
ReferanceID IS NULL
UNION ALL
SELECT
c.ID, c.[NO], c.Firstname, c.Lastname, c.ReferanceID,
CASE
WHEN ct.ReferanceNO = ''''
THEN CAST(ct.[NO] AS VARCHAR(MAX))
ELSE CAST(ct.[NO] AS VARCHAR(MAX))
END
FROM
dbo.Consultants c
INNER JOIN
cte ct ON ct.ID = c.ReferanceID
)
SELECT *
FROM cte '
+ #SearchQuery
+ ' ORDER BY '
+ #SortDataField + ' ' + #SortOrder
+ ' OFFSET '+ CAST(#PageNum AS VARCHAR(20)) + ' ROW FETCH NEXT ' +CAST(#PageSize AS VARCHAR(20)) + ' ROWS ONLY'
EXEC sp_executesql #sql, N'#SearchQuery VARCHAR(MAX), #SortDataField VARCHAR(100), #SortOrder VARCHAR(4), #PageNum INT, #PageSize INT', #SearchQuery, #SortDataField, #SortOrder, #PageNum, #PageSize
END
I am trying to add this stored procedure to Entity Framework, but without success. Entity Framework can't create a complex type for my stored procedure, I click on the "Get Column Information" button, but the text box below says "The selected stored procedure returns no columns".
Do you know what is the problem?
P.S. It works if I remove parameters from #sql string

Probably because there is no proper SQL Statement in your procedure through which Entity Framework can detect the resulting columns.
A workaround is, to put a SQL Select statement at the end of the procedure which should tell Entity Framework about the result, for example just put following statement at the end of the procedure and update it
SELECT
CAST(1 AS int) AS ID
,CAST(1 AS int) AS [NO]
,N'Fist Name' AS Firstname
,N'Lasat Name' AS Lastname
,CAST(1 AS int) AS ReferanceID
,CAST('' AS VARCHAR(MAX)) AS ReferanceNO
After this, go in Entity Framework and import the procedure, it should show you the columns properly. Once you imported the SP in Entity Framework, go back in Procedure and comment out the last SELECT statement which we just added. This last statement is just for Entity Framework to understand what SP is going to return.
P.S. I don't know exactly the column types, so you should better change the column types in the select statement

Related

SQL Server – pulling user data using dynamic query in stored procedure

I have problem, I'm having trouble viewing user information using stored procedure. The procedure accepts three parameters: table, column and searchBySomething. Every time I want to search for a user using another column, the column variable receives the column of the id and the searchBySomething variable receives specific id, the procedure is work but when I'm send another column I'm get the error message
Invalid column name (the data)
The procedure looks like this :
ALTER PROCEDURE [dbo].[userDetailsDisplay]
#table NVARCHAR(30),
#column NVARCHAR(30),
#searchBySomething NVARCHAR(30)
DECLARE #sql NVARCHAR(100)
SET #sql = 'SELECT * FROM ' + #table + ' WHERE ' + #column + ' = ' + #searchBySomething
EXECUTE sp_executesql #sql
So the specific error you're getting is because you're not checking the input to see if the string being passed into #column actually exists. You can check for it's existence against the metadata catalog view sys.columns doing something like this:
if not exists
(
select 1
from sys.columns
where object_id = object_id(#table)
and name = #column
)
begin
raiserror('Column %s does not exist in table %t', 16, 1, #column, #table)
return
end
However I would be remiss if I didn't point out two things.
First, this dynamic table dynamic where clause pattern is very bad practice. If it's for someone who already has database access, they can simply query the tables themselves. And if it's for an external user, well, you've basically given them full database read access through this procedure. Of course there are some rare occasions where this pattern is needed, so if you're dead set on using dynamic sql, that leads me to my next point.
The code you've written is vulnerable to a SQL injection attack. Any time you use dynamic SQL you must be VERY careful how it's constructed. Say I passed in the column name ; drop database [admin]-- Assuming you had such a database, my could would happily be executed and your database would disappear.
How to make dynamic SQL safe is a complex topic, but if you're serious about learning more about it, this is probably one of the best articles you can find. http://www.sommarskog.se/dynamic_sql.html
By parameterizing your query and using quotename() on the table and column, I modified it to look like this. This will still throw weird errors if someone tries to do an injection attack, but at least it wont actually execute their code.
create procedure [dbo].[userDetailsDisplay]
#table nvarchar(30),
#column nvarchar(30),
#searchBySomething nvarchar(30)
as
begin
declare
#sql nvarchar(max),
#params nvarchar(1000)
if not exists
(
select 1
from sys.columns
where object_id = object_id(#table)
and name = #column
)
begin
raiserror('Column %s does not exist in table %t', 16, 1, #column, #table)
return
end
select #sql = '
select *
from ' + quotename(#table) + ' WHERE ' + quotename(#column) + ' = #searchBySomething'
execute sp_executesql
#stmt = #sql,
#params = '#searchBySomething nvarchar(30)',
#searchBySomething = #searchBySomething
end
Just check to make sure that the column exist in the table.
for each #table called, check that the #column variable is in that table.
SET #sql = 'SELECT * FROM ' + #table + ' WHERE ' + #column + ' = ' +''' #searchBySomething +''''
Ex : select * from table where column ='value'

Dumping indexes to a SQL script programmatically [duplicate]

Does anyone have a script to list of CREATE INDEX statements for all existing indexes in a SQL Server database?
This thread List of all index & index columns in SQL Server DB has great tips on how to find them. But a script to generate the CREATE INDEX statements would be great. Sometimes we come into a situation without adequate data, or indexes have been added in an ad-hoc manner over time without documentation, so the create statements are missing. Like in a situation I find myself in right now.
Thanks.
Use Generate Scripts from SQL Management Studio and choose the "Script Indexes" options (under Advanced Scripting options)
I wrote something for that a while ago. You might have to modify it for your needs, but at least you have a skeleton.
if exists (select 1 from information_schema.routines where routine_name = 'Script_CreateIndex')
drop proc Script_CreateIndex
go
create proc Script_CreateIndex (
#TableName varchar(124)
)
as
begin
if not exists (select 1 from sys.indexes where object_name(object_id) = #TableName and type_desc in ('CLUSTERED', 'NONCLUSTERED'))
return
declare #IndexList table (
Id int identity,
IndexName varchar(124),
IndexDescription varchar(max),
IndexKeys varchar(max)
)
insert #IndexList(IndexName, IndexDescription, IndexKeys)
exec sp_helpindex #TableName
if (select count(*) from #IndexList) > 0
begin
select '-- Creating indexes for table ' + #TableName
while exists (select 1 from #IndexList)
begin
declare #Id int, #IndexName varchar(124), #IndexDescription varchar(max), #IndexKeys varchar(max)
select top 1 #Id = Id, #IndexName = IndexName, #IndexDescription = IndexDescription, #IndexKeys = IndexKeys from #IndexList order by Id
delete from #IndexList where Id = #Id
declare #Clustered varchar(10), #Unique varchar(7)
select #Clustered = case when patindex('%nonclustered%', #IndexDescription) > 0 then '' else ' clustered ' end
select #Unique = case when patindex('%unique%', #IndexDescription) > 0 then ' unique ' else '' end
select 'if not exists (select 1 from sys.indexes where name = ''' + #IndexName + ''')'
select 'begin'
select char(9) + 'create' + #Unique + #Clustered + ' index [' + #IndexName + '] on [' + #TableName + '](' + #IndexKeys + ')'
select char(9) + 'select ''Index ' + #IndexName + ' created.'''
select 'end'
select 'go'
end
select ''
select ''
end
end
go
grant exec on Script_CreateIndex to public
select 'Script_CreateIndex compiled.' 'Job'
go
Check my solution here:
https://stackoverflow.com/a/55742250/1831734
Output
Create Drop Rebuild
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE CLUSTERED INDEX [PK_Table1] ON [Table1] ( [Tab1_ID] ) DROP INDEX [PK_Table1] ON [Table1] ALTER INDEX [PK_Table1] ON [Table1] REBUILD
CREATE UNIQUE INDEX [IX_Table1_Name] ON [Table1] ( [Tab1_Name] ) DROP INDEX [IX_Table1_Name] ON [Table1] ALTER INDEX [IX_Table1_Name] ON [Table1] REBUILD
CREATE NONCLUSTERED INDEX [IX_Table2] ON [Table2] ( [Tab2_Name], [Tab2_City] ) INCLUDE ( [Tab2_PhoneNo] ) DROP INDEX [IX_Table2] ON [Table2] ALTER INDEX [IX_Table2] ON [Table2] REBUILD
You can do it on a table by table basis by using the "Object Explorer" window
Go to the Indexes folder in Management studio, highlight the folder then open the Object Explorer pane
You can then "shift Select" all of the indexes on that table, if you right click to script "CREATE TO" it will create a script with all the relevant indexes for you.

How to pass string array of Columns as parameter to SQL Server Stored procedure

I have a comma delimited string with column names i.e.
"Column1, Column2, Column3" which will be passed as a parameter to the following stored procedure.
My Stored Procedure is as follows:
#ColNames VARCHAR(1000)
Select ROW_NUMBER() OVER (ORDER BY Column1, Column2, Column3) AS RowNum,
Column1, Column2, Column3
INTO #Results
From Table A
I want to replace the above hardcoded column names with the parameter #colNames. This will include splitting the comma delimited parameter into the individual column names.
You'd need to use dynamic SQL in this case and execute it with sp_executesql.For example:
DECLARE #ColNames VARCHAR(1000)
DECLARE #sSQL NVARCHAR(MAX)
SET #ColNames ='Column1, Column2,...'
SET #sSQL = 'SELECT ' + #ColNames + ' FROM myTable....'
exec sp_executesql #sSQL
I also made a quick SQLFiddle to this.
You may try this :
CREATE PROCEDURE yourId (#columns VARCHAR(1000))
AS
BEGIN
DECLARE #s_query VARCHAR(MAX)
SET #s_query = 'Select ROW_NUMBER() OVER (ORDER BY ' + #columns + ') AS RowNum, ' + #columns + ' From A'
EXEC(#s_query)
END
this is for t sql syntax
If you need information on how to split a string, you may have a look on this thread : Split function equivalent in T-SQL?

How do I get the correct sql quoted string for a .NET DbType

I want to run an ALTER TABLE that adds a default value constraint to a column.
I generate this statement dynamically from a .NET program.
How do I best format and quote the value when building my sql - now that ALTER TABLE statements does not support parameters (Gives the error 'Variables are not allowed in the ALTER TABLE statement').
Is there a utility for that in .NET? Or another solution?
You can do this in TSQL; for example, say you parameterize the command, passing in #DefaultValue, a varchar which may or may not be a valid TSQL literal. Because we are writing DDL, we will need to concatenate and exec, however we clearly don't want to blindly concatenate, as the value could be illegal. Fortunately, quotename does everything we need. By default, quotename outputs [qualified object names], but you can tell it to operate in literal escaping mode, for both single-quote and double-quote literals.
So our query that accepts #DefaultValue can build an SQL string:
declare #sql nvarchar(4000) = 'alter table ...';
-- ... blah
-- append the default value; note the result includes the outer quotes
#sql = #sql + quotename(#DefaultValue, '''');
-- ... blah
exec (#sql);
Full example:
--drop table FunkyDefaultExample
create table FunkyDefaultExample (id int not null)
declare #tableName varchar(20) = 'FunkyDefaultExample',
#colName varchar(20) = 'col name',
#defaultValue varchar(80) = 'test '' with quote';
-- the TSQL we want to generate to exec
/*
alter table [FunkyDefaultExample] add [col name] varchar(50) null
constraint [col name default] default 'test '' with quote';
*/
declare #sql nvarchar(4000) = 'alter table ' + quotename(#tablename)
+ ' add ' + quotename(#colName) + 'varchar(50) null constraint '
+ quotename(#colName + ' default') + ' default '
+ quotename(#defaultValue, '''');
exec (#sql);
-- tada!
string.Format("alter table YourTable add constraint DF_YourTable_Col1 default '{0}'",
inputValue.Replace("'", "''"));

SQL Server (2008) Pass ArrayList or String to SP for IN()

I was wondering how I can pass either an ArrayList, List<int> or StringBuilder comma delimited list to a stored procedure such that I find a list of IDs using IN():
#myList varchar(50)
SELECT *
FROM tbl
WHERE Id IN (#myList)
In C# I am currently building the list as a string which is comma delimeted; however when using nvarchar(50) for example, as the type for the param in the stored procedure - I get an error as it can't convert '1,2,3' to int which it expects between the IN().
Any ideas? Much appreciated.
Pete
You could use a User Defined function such as
CREATE function [dbo].[csl_to_table] ( #list nvarchar(MAX) )
RETURNS #list_table TABLE ([id] INT)
AS
BEGIN
DECLARE #index INT,
#start_index INT,
#id INT
SELECT #index = 1
SELECT #start_index = 1
WHILE #index <= DATALENGTH(#list)
BEGIN
IF SUBSTRING(#list,#index,1) = ','
BEGIN
SELECT #id = CAST(SUBSTRING(#list, #start_index, #index - #start_index ) AS INT)
INSERT #list_table ([id]) VALUES (#id)
SELECT #start_index = #index + 1
END
SELECT #index = #index + 1
END
SELECT #id = CAST(SUBSTRING(#list, #start_index, #index - #start_index ) AS INT)
INSERT #list_table ([id]) VALUES (#id)
RETURN
END
Which accepts an nvarchar comma separated list of ids and returns a table of those ids as ints. You can then join on the returned table in your stored procedure like so -
DECLARE #passed_in_ids TABLE (id INT)
INSERT INTO #passed_in_ids (id)
SELECT
id
FROM
[dbo].[csl_to_table] (#your_passed_in_csl)
SELECT *
FROM
myTable
INNER JOIN
#passed_in_ids ids
ON
myTable.id = ids.id
In SQL 2008 there are table-valued-parameters, that make a friendly alternative to parsing CSV; see here for an example.
Otherwise, another option is xml - the xml data type in SQL Server allows you to read this pretty easily (although it takes more transfer bytes).

Categories

Resources