Table Value optional parameter - c#

Is it possible to create a procedure with a table value parameter as an optional parameter.
I tried the following code:
CREATE PROCEDURE SP
#Table testteype = null READONLY
AS
....
But I get this error:
Operand type clash: void type is incompatible with test type
ps: I use sql server with C#.Net

Table-Valued parameters always have an implicit value of an empty table. So you can actually call that procedure without any parameters and it would execute but the table would be empty.
So it doesn't really make sense to label a table-value parameter with a default value. Remove the "=null", check the table contents, and you should be good to go.

Basically, having default value "= null" makes no sense and is the reason of the error.
By default, #Table testteype gets value of an empty table. Thus, you may remove = null:
CREATE PROCEDURE SP
#Table testteype READONLY
AS
....
Reference: for a sample on how to use this with C# ADO.NET i would recommend to use this post - Using SQL Server’s Table Valued Parameters

--exa:
--create TYPE Table_tt as table(id int,name varchar(10))
create table #a (aid int,price int)
insert into #a (aid ,price )
select 1,10
union
select 2,50
create PROCEDURE SP
#Table_tt Table_tt null READONLY
AS
begin
select * into #tem from #Table_tt
select * from #a where aid in(select id from #tem) or aid=''
end
exec SP

Not sure why the answer above states making default value = NULL is incorrect but this works for me.
CREATE PROCEDURE SP
(
#Param1 VARCHAR(10),
#Param2 VARCHAR(10)=NULL
)
SELECT......
WHERE #Param1 = SOMETHING
AND (#Param2 = SOMETHING OR #Param2 IS NULL)

Related

Are there problems with SQL side white listing for Dynamic SQL?

I have an application where I want to produce historical charts from our database.
These charts can be many different parameters that are stored in our table, with a coupled datetime.
The stored procedure would contain something like:
CREATE PROCEDURE [dbo].[getLog]
-- Add the parameters for the stored procedure here
#getIdEntity INT = 0 ,
#columnName VARCHAR(100),
#startDate DATETIME2,
#endDate DATETIME2
AS
SELECT logs.[DateTime],
logs.#column1 as Property
from dbo.LogTable logs
WHERE logs.[IdEntity] = #IdEntity AND logs.[DateTime] >= #startDate
AND logs.[DateTime] <= #endDate
AND logs.column1 IS NOT NULL;'
This does not work, inserting a column as a parameter is not possible.
The answer was, of course, dynamic SQL, which obviously leads to a SQL injection problem.
the procedure then becomes:
ALTER PROCEDURE [dbo].[getLog]
-- Add the parameters for the stored procedure here
#getIdEntity INT = 0 ,
#columnName VARCHAR(100),
#startDate DATETIME2,
#endDate DATETIME2
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE
#dynamicSql NVARCHAR(500),
#timespan INT
SET #dynamicSql = '
SELECT [DateTime],
[' +#columnName+'] as Property from dbo.EntityTransactionLogEntry etl
WHERE etl.[IdEntity] = '+ CAST(#getIdEntity AS VARCHAR(10))
+ ' AND etl.[DateTime] >= '''+ (CONVERT (VARCHAR(50),#startDate,121)) +''''
+ ' AND etl.[DateTime] <= '''+ (CONVERT (VARCHAR(50),#endDate,121)) +''''
+ ' AND etl.[' +#columnName+'] IS NOT NULL;
EXEC (#dynamicSql)'
Which is obviously still vulnerable to SQL inject.
Is simply adding:
IF NOT EXISTS (SELECT * FROM
INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'EntityTransactionLogEntry'
AND COLUMN_NAME = #columnName)
BEGIN
PRINT 'Bad Input'
RETURN
END
(I also limit maximum time span that is query-able, but I'm not worried about that here)
before the Dynamic SQL portion sufficient to protect this from injection?
It is designed to fail in an invalid column name is supplied.
If yes, then presumably creating a view with only column names I approve of querying would be allowed to limit the columns that can be used?
I am specifically worried about injection, but I think I am safe, this will only be available from our API.
Is simply adding [a check in INFORMATION_SCHEMA] before the Dynamic SQL portion sufficient to protect this from injection?
By definition, no. What's happening is still SQL injection.
What you're asking, technically, is whether it's safe from malicious SQL injection.
Your checking INFORMATION_SCHEMA is a reasonable protection. It's a form of whitelisting, which is the technique we must use for things that can't be replaced by query parameters.
If there were a column in EntityTransactionLogEntry whose name could "break" the delimited identifier syntax, you'd be in trouble, but I assume you have control over the column names in your own table, and you wouldn't do that.
You should restrict your INFORMATION_SCHEMA query to your own TABLE_SCHEMA. As your query is currently written, it would be satisfied if any table catalogued in I_S has that column name.
Also, I'm not sure why you are not using query parameters in your terms for IdEntity and DateTime, where the values can be replaced by query parameters. You should do that.
Welcome Flippie
Tipically you can do some like
parameters..
.. paramterx varchar(100) = null,
paramtery varchar(100) = null
select *
from entitytransactionlog
where
logs.[IdEntity] = #IdEntity AND logs.[DateTime] >= #startDate
AND logs.[DateTime] <= #endDate
and (columnvalue = parameterx or parameterx is null)
and (columnvalue2 = parametery or parametery is null)
so sp is invoked like
exec sp parameterx = 'value'
the validation "OR" in bracket let every values if value in parameter is not defined

How to get custom SQL Server type into Entity Framework generated code

There is a TABLE Type defined in SQL server:
CREATE TYPE RealtySearchResult AS TABLE
(
realtyId int not null,
OwnerId int not null,
...)
And stored procedure:
CREATE PROCEDURE [dbo].[SearchRealty]
(#fulltext nvarchar(200) null,
#skipRows int,
#pageCount int,
....
)
AS
BEGIN
DECLARE #SQL nvarchar(max)
DECLARE #result RealtySearchResult
CREATE TABLE #TEMP
(
realtyId int not null,
OwnerId int not null,
...
)
set #SQL = N'
INSERT INTO #TEMP
SELECT
realty.Id AS realtyId,
realty.OwnerId,
....join with fulltext catalog.... WHERE....#pageCount .....#skipRows'
-- sp_executesql cannot write to local table variable #result,
-- that is why it reads to temp table and then to #result
exec sp_executesql #SQL, N'#skipRows int, #pageCount int', #skipRows, #pageCount
INSERT INTO #result SELECT * FROM #TEMP
SELECT * FROM #result
END
And then in Visual Studio I update the model from database and a new method (wrapper for store procedure SearchRealty) is generated, but it does not contains generated code for returning complex type.
I would expect that EntityFramework should be able to recognize that the store procedure returns defined table type RealtySearchResult and should generate wrapper for it. I am too lazy to write the complex return type by myself in C# again (I just wrote it in SQL). It is really needed?
Can I just generate wrapper for RealtySearchResult type in EntityFramework somehow?
I use Visual Studio 2017 and EntityFramework 6.
It sounds as duplicate as Stored procedure in Entity Framework database first approach but once I click the button Get Column Information I got message "The selected stored procedure or function returns no columns".
Analysis
Based on link Entity Framework not getting column info on a different schema provided by kirsten I realize that EntityFramework execute stored procedure with mode
SET FMTONLY ON
It means it strips all condition and dynamic SQL. This result in empty temporary table and procedure failing during receiving metadata from EntityFramework.
Solution
To help the designer to get metadata without dynamic SQL. Count with that conditions are removed. Following code does a job:
DECLARE #result RealtySearchResult
IF 0=1
BEGIN
SELECT * FROM #result
RETURN
END
During execution of store procedure by EntityFramework (in order to get metadata), condition 0=1 is removed and empty table of Table type is returned which is enough to get metadata. This code is never trigerred in production because of impossible condition.

How to pass an array of <List> to a stored procedure

I have a list ClaimData in C# and it has three items Date, Type and Description
There can be multiple rows in this as below,
ClaimData
Date Type Description
01/02/2012 "Medical" "Its a medical"
05/02/2013 "Theft" "Its a Theft"
01/02/2014 "Test" "Its a Test"
I want to pass this whole data to a stored procedure in one go to the sql server, so that I can reduce the database hits. I have written stored procedure which can iterate through this list and insert them in a table.
How to achieve by manipulating the list object could be passed to the stored procedure as a parameter?
You will need to do a couple of things to get this going, since your parameter is getting multiple values you need to create a Table Type and make your store procedure accept a parameter of that type.
Since you are passing a TABLE as a parameter you will need to create a TABLE TYPE something as follows
TABLE TYPE
CREATE TYPE dbo.ClaimData AS TABLE
(
[Date] DATE
[Type] VARCHAR(50)
[Description] VARCHAR(100)
)
GO
Stored Procedure to Accept That Type Param
CREATE PROCEDURE mainValues
#TableParam ClaimData READONLY --<-- Accepts a parameter of that type
AS -- Note it is ReadOnly
BEGIN
SET NOCOUNT ON;
--Temp table to store the passed values
-- since the passed parameter is only Read only and you
-- cannot make any changes to the parameter so if you need to
-- manipulate the data inside parameter you will need to get it
-- into a Table vaiable.
-- Declare a Table variable
DECLARE #tmp_values table(
[Date] DATE
[Type] VARCHAR(50)
[Description] VARCHAR(100)
);
--Get values into that Table variable
INSERT INTO #tmp_values ([Date],[Type],[Description])
SELECT [Date],[Type],[Description] FROM #TableParam
-- Do other cool stuff with your passed data
SELECT * FROM #tmp_values --<-- For testing purpose
END
EXECUTE PROC
Declare a variable of that type and populate it with your values.
DECLARE #Table ClaimData( --<-- Declare a variable of your type
[Date] DATE
[Type] VARCHAR(50)
[Description] VARCHAR(100)
);
-- Populate the variable
INSERT INTO #Table ([Date],[Type],[Description])
SELECT [Date],[Type],[Description] FROM Source_Table
EXECUTE mainValues #Table --<-- Stored Procedure Executed
I sent all three column as a string using string builder and delimeter '|'
DateString = '01/02/2012|05/02/2013|01/02/2014'
TypeString = 'Medical|Theft|Test'
DescString = "Its a medical|..."
On database side I used a function to delimit these strings and inserted all these values in a temp table. This solved my problem.

LINQ to SQL auto-generated type for stored procedure

I have the following stored procedure
ALTER PROCEDURE [dbo].Test
AS
BEGIN
CREATE TABLE ##table
(
ID1 int,
ID2 int
)
DECLARE #query varchar(MAX);
INSERT INTO ##table VALUES(1, 1);
SELECT * FROM ##table;
END
And I try to use it from C# code. I use LINQ to SQL as an O/RM. When I add the procedure to DataBaseContext it says that it can't figure out the return value of this procedure. How to modify the stored procedure so that I can use it with LINQ to SQL.
Note: I need to have global template table!
for MSSQL use SET FMTONLY OFF; at the begining of the proc. Tells SQL to run the stored proc not to make assumptions, which is the only way I've learned to make LINQ work when using temp tables.
Try identifying the columns in your select.
SELECT ID1, ID2 FROM ##table;

How to check stored procedure return value elegantly

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

Categories

Resources