I have this procedure
CREATE PROCEDURE dbo.spProcedure1
@intArray as dbo.intArray READONLY
AS
BEGIN
-- ...
END
which use user type as a parameter
CREATE TYPE dbo.IntArray AS TABLE (IntValue int NULL)
and I am calling the procedure from the C# ASP.NET MVC 4 project
// creating empty SQL @IntArray parameter
var emptyIntDataTable = new DataTable();
emptyIntDataTable.Columns.Add("IntValue");
// calling stored procedure
return Database.SqlQuery<int>(
@"spProcedure1 @p1",
new SqlParameter("p1", (object)Utils.ToDataTable(m.IntArray) ?? emptyIntDataTable)
).ToList();
// ToDataTable method which is returning null
public static DataTable ToDataTable<T>(this IList<T> data)
{
if (data == null)
return null;
... // code omitted because it is not working yet
}
the error which is throwed when calling stored procedure is
The table type parameter 'p1' must have a valid type name.
How to pass an empty table value?
Passing the list instead of datatable throw following error
var emptyIntDataTable = new List<int>;
No mapping exists from object type System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] to a known managed provider native type.
In your code:
where it says:
return Database.SqlQuery<int>(
@"spProcedure1 @p1", new SqlParameter("p1",
(object)Utils.ToDataTable(m.IntArray) ?? emptyIntDataTable)
).ToList();
Change it to read:
return m.IntArray.Length > 0?
Database.SqlQuery<int>(@"spProcedure1 @p1",
new SqlParameter("p1",
(object)Utils.ToDataTable(m.IntArray))).ToList():
Database.SqlQuery<int>(@"spProcedure1")).ToList();
sample to show how to not pass table parameter
CREATE TYPE dbo.KeyIds]
AS TABLE(pkId int NOT NULL,
PRIMARY KEY CLUSTERED (pkId ASC)
WITH (IGNORE_DUP_KEY = OFF))
Go
-- ------------------------------
Create procedure testProc
@aIds dbo.keyIds readonly
as
Set NoCount On
if exists (select * from @aIds)
Select * from @aIds
else
Select 'No Aids passed in'
Go
-- ------------------------------
Exec dbo.testProc -- <--- Here I am NOT passing the @aids parameter
But, even though I am NOT passing the @aids parameter
it still works, and the subquery (select * from @aIds)
still functions, and since it is an empty datatable the SP returns the empty message 'No Aids passed in'.
On the other hand, if you pass the parameter
Declare @MyIds dbo.keyIds
Insert @MyIds Values(1)
Insert @MyIds Values(2)
Insert @MyIds Values(3)
Insert @MyIds Values(4)
Insert @MyIds Values(5)
Exec dbo.testProc @MyIds -- <--- Here I AM passing the @aids parameter
it outputs the contents of the datatable parameter
C# code example...
public DataTable GetAccountTransactions(IEnumerable<int> accountIds)
{
const string procName = "FetchAccountTransactionData";
var acctIds = accountIds == null ?
new List<int>() : accountIds.ToList();
// -------------------------------------------------
var parms = DbParamList.Make();
// DbParamList is a List<IDbDataParameter>
// See here, ONLY ADD PARAMETER if list is NOT empty!
if (acctIds.Count > 0)
parms.AddSQLTableParm("aIds", acctIds);
try
{ // following constructs command obkect and calls SP
return Utilities.GetDataTable(schemaNm + "." +
procName, parms, copConn);
}
catch (SqlException dbX)
{
// Exception stuff
}
}
public class DbParamSortedList : SortedList<string,IDbDataParameter> { }