Database call throwing unexpected error in C# - c#

I have written the following method to get a list of objects from database:
public IEnumerable<ProductDBKey6> AssignProductKey6 (string strProdList)
{
List<ProductDBKey6> lstProdDBKey6 = new List<ProductDBKey6>();
// Create the parameters collection
var parameters = new Collection<SqlParameter>();
// Add each parameter. Entity will not work without all params in the correct order
SqlParameter param = StoredProcedureParameterBuilder.StringParam("#ProductID", strProdList, -1);
int? tempTimeout = this.Database.CommandTimeout;
this.Database.CommandTimeout = 300;
lstProdDBKey6 = this.Database.SqlQuery<ProductDBKey6>("spc.FindProductDBKeyForID", param).ToList();
this.Database.CommandTimeout = tempTimeout;
return lstProdDBKey6;
}
But I am receiving the error that #ProductID parameter is not supplied. Following is the code written in StoredProcedureParameterBuilder class:
internal static class StoredProcedureParameterBuilder
{
internal static SqlParameter StringParam(string paramName, string paramValue, int paramSize)
{
SqlParameter outParam = new SqlParameter
{
ParameterName = paramName,
SqlDbType = System.Data.SqlDbType.VarChar,
Direction = System.Data.ParameterDirection.Input,
Size = paramSize
};
if (string.IsNullOrEmpty(paramValue))
{
outParam.Value = DBNull.Value;
}
else
{
outParam.Value = paramValue;
}
return outParam;
}
}
My stored procedure accepts only one parameter #ProductID. Following is the Stored Procedure declaration:
ALTER PROCEDURE [spc].[FindProductDBKeyForID] (
#ProductID VARCHAR(MAX)
)
Please help how to resolve the error.

Instead of this line:
lstProdDBKey6 =
this
.Database.SqlQuery<ProductDBKey6>("spc.FindProductDBKeyForID", param)
.ToList();
Try this:
lstProdDBKey6 =
this
.Database.SqlQuery<ProductDBKey6>("exec spc.FindProductDBKeyForID #ProductID", param)
.ToList();

Related

How can i call OUT parameter in PROCEDURE via Npgsql

How can i call OUT parameter in PROCEDURE via Npgsql
I am getting this error when i run below code
-- PostgreSQL 14 -> PROCEDURE
CREATE OR REPLACE PROCEDURE sp_add_users_new(arrJsnUsers JSON[], jOut OUT JSON) AS $$
DECLARE
intSpStatus INT;
v json;
BEGIN
FOREACH v IN ARRAY arrJsnUsers
LOOP
INSERT INTO tbl_user (vhr_name, vhr_password, sin_record_status)
VALUES(v->>'strName', v->>'strPassword', (v->>'intRecordStatus')::SMALLINT);
END LOOP;
COMMIT;
intSpStatus = 1;
jOut['intSpStatus'] = intSpStatus;
END;
$$ LANGUAGE plpgsql
--Dot net core 5
using (NpgsqlConnection objCon = new NpgsqlConnection("..."))
{
objCon.Open();
using (NpgsqlCommand objSqlCommand = new NpgsqlCommand("CALL sp_add_users_new(:p1, :p2)", objCon))
{
// Parameters
NpgsqlParameter[] lstSqlParameter = {
new NpgsqlParameter("p1", NpgsqlDbType.Array|NpgsqlDbType.Json) {
Value = lstUsers,
DataTypeName = "arrJsnUsers"
},
new NpgsqlParameter("p2", NpgsqlDbType.Json) {
Value = jOut,
DataTypeName = "jOut",
Direction = ParameterDirection.Output
},
};
objSqlCommand.Parameters.AddRange(lstSqlParameter);
// Execute
IDataReader objDataReader = objSqlCommand.ExecuteReader();
}
objCon.Close();
}
--Error
42703: column "p2" does not exist
Below is a sample which works - it's recommended to read the Npgsql docs on this. Some comments:
You don't specify the output parameter in SQL - just pass a NULL as per the PostgreSQL docs.
Since #p2 is an output parameter, it makes no sense to specify its value (jOut1 above).
DataTypeName (jOut above) also doesn't make sense here; that's used to indicate to Npgsql which PG type to send for input parameters (similar to NpgsqlDbType).
using var objCon = new NpgsqlConnection("Host=localhost;Username=test;Password=test");
objCon.Open();
using var objSqlCommand = new NpgsqlCommand("CALL sp_add_users_new(#p1, NULL)", objCon);
// Parameters
var lstUsers = new[] { #"{ ""Foo"": ""Bar"" }" };
NpgsqlParameter[] lstSqlParameter = {
new NpgsqlParameter("p1", NpgsqlDbType.Array|NpgsqlDbType.Json) { Value = lstUsers, DataTypeName = "arrJsnUsers"},
new NpgsqlParameter("p2", NpgsqlDbType.Json) { Direction = ParameterDirection.Output},
};
objSqlCommand.Parameters.AddRange(lstSqlParameter);
// Execute
IDataReader objDataReader = objSqlCommand.ExecuteReader();
Console.WriteLine(objSqlCommand.Parameters[1].Value);

Count line in SQL query

I try to count line in my SQL query.
It works without parameter. For example, if I assign directly FIOForm = 'SmithJJ'. I really don't understand what I'm doing wrong.
Exception: the SqlParameter is already contained by another SqlParameterCollection
int kolNar = 0;
System.Data.SqlClient.SqlParameter Name = new System.Data.SqlClient.SqlParameter("#Name", System.Environment.UserName);
var pushStat = db.Database.SqlQuery<Reestr>("select * from Reestr where FIOForm = #Name and Status = 'Executed'", Name);
foreach (var u in pushStat)
{
kolNar = pushStat.Count();
}
if (kolNar > 0)
MessageBox.Show(kolNar.ToString());
I suppose you can call:
Dispose();
before
System.Data.SqlClient.SqlParameter Name = new System.Data.SqlClient.SqlParameter("#Name", System.Environment.UserName);

How To Pass a List Object From C# to an Oracle Stored Procedure?

I'm trying to send a List Object from my C# WebService method over to my stored procedure in Oracle.
Before posting here, I've tried all suggested duplicate links. Here's what I've accomplished so far:
Success: In C#, I can pass my List values from my HTML page over to my WebService method.
Success: In Oracle, I have created a Table, Object Type, Table Type, and Stored Procedure to accept the List values. I was able to test this using an Anonymous block and sample data.
Problem: I cannot get to pass my List values from my C# WebMethod over to my Oracle Stored Procedure.
I'm currently using the following setup:
Visual Studio 2017
.NET Framework 4.6.1
Oracle.ManagedDataAccess 18.6.0
Keep in mind that the version of Oracle.ManagedDataAccess 18.6.0 does NOT contain the OracleDbType.Array as suggested in the older examples.
public class Automobile
{
public string Make { get; set; }
public string Model { get; set; }
public string Year { get; set; }
public string Country { get; set; }
}
using Oracle.ManagedDataAccess.Client;
using Oracle.ManagedDataAccess.Types;
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string InsertCars(List<Automobile> myCars, int userID)
{
DataSet dataSet = new DataSet();
using (OracleConnection sqlConnection = new OracleConnection(OracleDBConnection))
{
using (OracleCommand sqlCommand = new OracleCommand("sp_InsertCars", sqlConnection))
{
sqlConnection.Open();
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.Parameters.Add(
new OracleParameter
{
CollectionType = OracleCollectionType.PLSQLAssociativeArray,
Direction = ParameterDirection.Input,
ParameterName = "p_CarList",
UdtTypeName = "tt_Automobile",
Size = myCars.Count,
Value = myCars.ToArray()
}
);
sqlCommand.Parameters.Add(
new OracleParameter
{
OracleDbType = OracleDbType.Int32,
Direction = ParameterDirection.Input,
ParameterName = "p_UserID",
Value = userID
}
);
sqlCommand.Parameters.Add(
new OracleParameter
{
OracleDbType = OracleDbType.RefCursor,
Direction = ParameterDirection.Output,
ParameterName = "o_Cursor"
}
);
using (OracleDataAdapter sqlAdapter = new OracleDataAdapter(sqlCommand))
{
sqlAdapter.SelectCommand = sqlCommand;
sqlAdapter.Fill(dataSet);
}
}
return JsonConvert.SerializeObject(dataSet);
}
}
CREATE TABLE tblCars
(
RecordID INT GENERATED BY DEFAULT AS IDENTITY NOMINVALUE NOMAXVALUE INCREMENT BY 1 START WITH 1 NOCACHE NOCYCLE NOORDER,
Make NVARCHAR2(100) NULL,
Model NVARCHAR2(100) NULL,
Year NVARCHAR2(4) NULL,
Country NVARCHAR2(100) NULL,
UserID INT NULL
);
CREATE OR REPLACE TYPE ot_Automobile AS OBJECT
(
Make varchar2(100),
Model varchar2(100),
Year varchar2(4),
Country varchar2(100)
);
CREATE OR REPLACE TYPE tt_Automobile AS TABLE OF ot_Automobile;
CREATE OR REPLACE PROCEDURE sp_InsertCars
(
p_CarList In tt_Automobile,
p_UserID In integer,
o_Cursor Out Sys_RefCursor
)
AS
BEGIN
DBMS_Output.Enable;
For RowItem In (Select * From Table(p_CarList))
Loop
Insert Into tblCars
(
Make,
Model,
Year,
Country,
UserID
)
Values(
RowItem.Make,
RowItem.Model,
RowItem.Year,
RowItem.Country,
p_UserID
);
End Loop;
-- Return our results after insert
Open o_Cursor For
Select Make, Model, Year, Country From tblCars Where UserID = p_UserID;
EXCEPTION
When Others Then
DBMS_Output.Put_Line('SQL Error: ' || SQLERRM);
END sp_InsertCars;
COMMIT
/
The result should allow me to pass my array Object from my WebService WebMethod over to my Oracle stored procedure and then loop through each item of the array to perform an Insert.
Here's an example of the data I'm trying to pass in.
this answer depends on commercial package, but if you're as desperate as i am, it's a lifesaver for very reasonable $300 (circa 2020-Q4)... scouts honor, i'm no shill
DevArt's Oracle provider makes elegant work of passing lists of objects to stored procs... it really works... .net core 3.1 compatible, tested on linux, no dependencies on native oracle client ... see my take on a working console app sample below based on the linked forum post
DevArt's "OracleType.GetObjectType()" API makes the UDT marshalling part of this incredibly trivial for us... way less to grok than the existing unmanaged ODP table type support samples i've seen out there for years
strategic consideration - if you already have a sizable code base on an oracle provider, consider just leaving all that code as-is and only take on regression testing this new dependency where the specialized table type support is actually needed
short and sweet sample for quick digestion
using System;
using System.Data;
using Devart.Data.Oracle;
namespace ConsoleApp1
{
class Program
{
private static int oraTable;
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
//good docs:
//direct connection: https://www.devart.com/dotconnect/oracle/docs/StoredProcedures-OracleCommand.html
//linux licensing: https://www.devart.com/dotconnect/oracle/docs/?LicensingStandard.html
using OracleConnection db = new OracleConnection("{insert yours}");
//devart trial licensing nutshell... on WINDOWS, download & run their installer...
//the only thing you really need from that install is the license key file dropped here:
// %programdata%\DevArt\License\Devart.Data.Oracle.key
// then just go nuget the "Devart.Data.Oracle" package reference
//on Windows, the trial license file gets automatically picked up by their runtime
//if you're on Linux, basically just read their good instructions out there
//i've just tested it on WSL so far and plan to try on Azure linux app svc within next day
//db.ConnectionString = "license key=trial:Devart.Data.Oracle.key;" + db.ConnectionString;
db.Direct = true; //nugget: crucial!! https://www.devart.com/dotconnect/oracle/docs/DirectMode.html
db.Open();
var cmd = db.CreateCommand("UserPermissions_u", CommandType.StoredProcedure);
cmd.DeriveParameters();
//tblParm.OracleDbType = OracleDbType.Table;
//passing "table" type proc parm example: https://forums.devart.com/viewtopic.php?t=22243
var obj = new OracleObject(OracleType.GetObjectType("UserPerm", db));
var tbl = new OracleTable(OracleType.GetObjectType("UserPerms", db));
obj["UserPermissionId"] = "sR1CKjKYSKvgU90GUgqq+w==";
obj["adv"] = 1;
tbl.Add(obj);
cmd.Parameters["IN_Email"].Value = "banderson#kingcounty.gov";
cmd.Parameters["IN_Permissions"].Value = tbl;
cmd.ExecuteNonQuery();
//"i can't believe it's not butter!" -me, just now =)
}
}
}
corresponding oracle db definitions:
create or replace type UserPerm as object ( UserPermissionId varchar2(24), std number(1), adv number(1) );
create or replace type UserPerms as table of UserPerm;
create or replace PROCEDURE UserPermissions_u (
IN_Email IN varchar2,
IN_Permissions IN UserPerms
) is
dummyvar number default 0;
begin
select count(*) into dummyvar from table(IN_Permissions);
end;
/
more elaborate shot at generically reflecting on an inbound object to hydrate the proc parms like the OP's request... take with caution, needs testing/bullet-proofing ... i'd love to get better alternatives if anybody cares to share
using System;
using System.Data;
using Devart.Data.Oracle;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections;
namespace ConsoleApp1
{
public class User
{
public string Email { get; set; }
public List<UserPermissionEffective> Permissions { get; set; }
}
public class UserPermissionEffective
{
public string UserPermissionId { get; set; }
public string Email { get; set; }
public bool Std { get; set; }
public bool Adv { get; set; }
public string Mod { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var dto = new User { Email = "testy#mctesterson.com", Permissions = new List<UserPermissionEffective> {
new UserPermissionEffective { UserPermissionId = "1", Std = false, Adv = true },
new UserPermissionEffective { UserPermissionId = "2", Std = true, Adv = false }
} };
if (dto == null) return;
//good docs:
//direct connection: https://www.devart.com/dotconnect/oracle/docs/StoredProcedures-OracleCommand.html
//linux licensing: https://www.devart.com/dotconnect/oracle/docs/?LicensingStandard.html
var dbstring = Environment.GetEnvironmentVariable("dbstring");
using OracleConnection db = new OracleConnection(dbstring);
db.ConnectionString = "license key=trial:Devart.Data.Oracle.key;" + db.ConnectionString;
db.Direct = true; //nugget: crucial!! https://www.devart.com/dotconnect/oracle/docs/DirectMode.html
db.Open();
var cmd = db.CreateCommand("UserPermissions_u", CommandType.StoredProcedure);
cmd.DeriveParameters();
//regex gets everything following the last underscore. e.g. INOUT_PARMNAME yields PARMNAME
var regex = new Regex(#"([^_\W]+)$", RegexOptions.Compiled);
//get the inboud model's root properties
var dtoProps = dto.GetType().GetProperties();
//loop over all parms assigning model properties values by name
//going by parms as the driver versus object properties to favor destination over source
//since we often ignore some superfluous inbound properties
foreach (OracleParameter parm in cmd.Parameters)
{
var cleanParmName = regex.Match(parm.ParameterName).Value.ToUpper();
var dtoPropInfo = dtoProps.FirstOrDefault(prop => prop.Name.ToUpper() == cleanParmName);
//if table type, then drill into the nested list
if (parm.OracleDbType == OracleDbType.Table)
{
//the type we're assigning from must be a list
//https://stackoverflow.com/questions/4115968/how-to-tell-whether-a-type-is-a-list-or-array-or-ienumerable-or/4115970#4115970
Assert.IsTrue(typeof(IEnumerable).IsAssignableFrom(dtoPropInfo.PropertyType));
var listProperty = (dtoPropInfo.GetValue(dto) as IEnumerable<Object>).ToArray();
//don't bother further logic if the list is empty
if (listProperty.Length == 0) return;
//get the oracle table & item Udt's to be instanced and hydrated from the inbound dto
var tableUdt = OracleType.GetObjectType(parm.ObjectTypeName, db);
var itemUdt = OracleType.GetObjectType(tableUdt.ItemObjectType.Name, db);
var dbList = new OracleTable(tableUdt);
//and the internal list item objects
var subPropInfos = dtoPropInfo.PropertyType.GenericTypeArguments[0].GetProperties().ToDictionary(i=>i.Name.ToUpper(), i=>i);
//for every item passed in...
foreach (var dtoSubItem in listProperty) {
//create db objects for every row of data we want to send
var dbObj = new OracleObject(itemUdt);
//and map the properties from the inbound dto sub items to oracle items by name
//using reflection to enumerate the properties by name
foreach (OracleAttribute field in itemUdt.Attributes)
{
var val = subPropInfos[field.Name.ToUpper()].GetValue(dtoSubItem);
//small tweak to map inbound booleans to 1's & 0's on the db since oracle doesn't support boolean!?!
var isDbBool = field.DbType == OracleDbType.Integer && field.Precision == 1;
dbObj[field] = isDbBool ? ((bool)val ? 1 : 0) : val;
}
//lastly add the db obj to the db table
dbList.Add(dbObj);
}
parm.Value = dbList;
}
else {
parm.Value = dtoPropInfo.GetValue(dto);
}
}
cmd.ExecuteNonQuery();
}
}
}
Please refer following link to setup ODAC
Setup Ref and use follwing link to get the ODAC
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
using System;
using System.Data;
namespace Strace_CustomTypes
{
class Program
{
static void Main(string[] args)
{
// Setup Ref - https://o7planning.org/en/10509/connecting-to-oracle-database-using-csharp-without-oracle-client
// ODAC 64bit ODAC122010Xcopy_x64.zip - https://www.oracle.com/technetwork/database/windows/downloads/index-090165.html
// .Net Framework 4
// 'Connection string' to connect directly to Oracle.
string connString = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=0.0.0.0)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=SIT)));Password=PASSWORD;User ID=USERID";
OracleConnection straceOracleDBConn = new OracleConnection(connString);
OracleCommand cmd = new OracleCommand("PKG_TEMP.TEST_ARRAY", straceOracleDBConn);
cmd.CommandType = CommandType.StoredProcedure;
try
{
straceOracleDBConn.Open();
CustomVarray pScanResult = new CustomVarray();
pScanResult.Array = new string[] { "hello", "world" };
OracleParameter param = new OracleParameter();
param.OracleDbType = OracleDbType.Array;
param.Direction = ParameterDirection.Input;
param.UdtTypeName = "USERID.VARCHAR2_ARRAY";
param.Value = pScanResult;
cmd.Parameters.Add(param);
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message} {Environment.NewLine} {ex.StackTrace}");
}
finally
{
straceOracleDBConn.Close();
cmd.Dispose();
straceOracleDBConn.Dispose();
}
Console.WriteLine("Press any key to exit");
Console.ReadLine();
}
}
//Ref https://www.codeproject.com/Articles/33829/How-to-use-Oracle-11g-ODP-NET-UDT-in-an-Oracle-Sto
public class CustomVarray : IOracleCustomType, INullable
{
[OracleArrayMapping()]
public string[] Array;
private OracleUdtStatus[] m_statusArray;
public OracleUdtStatus[] StatusArray
{
get
{
return this.m_statusArray;
}
set
{
this.m_statusArray = value;
}
}
private bool m_bIsNull;
public bool IsNull
{
get
{
return m_bIsNull;
}
}
public static CustomVarray Null
{
get
{
CustomVarray obj = new CustomVarray();
obj.m_bIsNull = true;
return obj;
}
}
public void FromCustomObject(OracleConnection con, IntPtr pUdt)
{
OracleUdt.SetValue(con, pUdt, 0, Array, m_statusArray);
}
public void ToCustomObject(OracleConnection con, IntPtr pUdt)
{
object objectStatusArray = null;
Array = (string[])OracleUdt.GetValue(con, pUdt, 0, out objectStatusArray);
m_statusArray = (OracleUdtStatus[])objectStatusArray;
}
}
[OracleCustomTypeMapping("USERID.VARCHAR2_ARRAY")]
public class CustomVarrayFactory : IOracleArrayTypeFactory, IOracleCustomTypeFactory
{
public Array CreateArray(int numElems)
{
return new string[numElems];
}
public IOracleCustomType CreateObject()
{
return new CustomVarray();
}
public Array CreateStatusArray(int numElems)
{
return new OracleUdtStatus[numElems];
}
}
}

The procedure "SP_name" has no parameter named "#typ_table" in dapper.net

What I Want.
If user enter duplicated value, then i want to delete that duplicate value and install the new one. I don't want to use update query because of some requirement.
Code:
Web API
public CoCApiResponse CreateVersion(CreateVersion versionObj)
{
var taskForDelete = Task.Run(() =>
versionRepository.DeleteVersions(objVersionCollection));
taskForDelete.Wait();
versionRepository.CreateVersion(versionObj.version, lstVersion).Result;
}
Repository:
public async Task<Version> DeleteVersions(CoCBaseCollection<Version> Versions)
{
List<Version> versionList = new List<Version>();
foreach (var objVersion in Versions)
{
Version version = new Version()
{
Id = objVersion.Id,
VersionName = objVersion.VersionName,
StructureWeek = objVersion.StructureWeek,
IsWLTP = objVersion.IsWLTP,
VersionStatusTypeId = objVersion.VersionStatusTypeId,
CreatedDateTime = System.DateTime.Now,
LastUpdatedDateTime = System.DateTime.Now
};
versionList.Add(version);
}
int retval = 0;
DataTable dtVersions = TableConversion.EnitiesToDataTable<Version>(versionList);
pars = new DynamicParameters();
pars.Add("#VersionTableForDelete", dtVersions.AsTableValuedParameter("dbo.VersionTable"));
pars.Add("#Retval", retval, DbType.Int32, ParameterDirection.Output);
string DeleteVersionSP = "dbo.DeleteVersions";
return await ExecSproc<Version>(DeleteVersionSP, pars, new Version()).ConfigureAwait(false);
}
public async Task<Version> CreateVersion(Version version, List<Version> versions)
{
int retval = 0;
DataTable dtVersions = TableConversion.EnitiesToDataTable<Version>(versions);
pars.Add("#refVersionTable", dtVersions.AsTableValuedParameter("dbo.VersionTable"));
pars.Add("#Retval", retval, DbType.Int32, ParameterDirection.Output);
string CreateVersionSP = "dbo.SPCreateVersion";
return await ExecSproc<Version>(CreateVersionSP, pars, version).ConfigureAwait(false);
}
Problem
I am getting this below error, while I am running the Delete and Create method
The procedure "SPCreateVersion" has no parameter named "#VersionTableForDelete".
What I have tried.
I have checked with my Store Procedure and it's working fine and also searched more than 2 hour from google But I am unable to resolve it. Because no one given the exact solution for dapper.net
Finally, What I found :
Yeah! It is working while doing delete and insert separately
public CoCApiResponse CreateVersion(CreateVersion versionObj)
{
versionRepository.CreateVersion(versionObj.version).Result;// working
}
//
public CoCApiResponse CreateVersion(CreateVersion versionObj)
{
var taskForDelete = Task.Run(() =>
versionRepository.DeleteVersions(versionObj));//working
taskForDelete.Wait();
}
But it is throwing the error while inserting, if we are doing both delete and insert.
public CoCApiResponse CreateVersion(CreateVersion versionObj)
{
var taskForDelete = Task.Run(() =>
versionRepository.DeleteVersions(versionObj));//working
taskForDelete.Wait();
versionRepository.CreateVersion(versionObj.version).Result;// Not Working and throwing an error
}
What I Guess
this error may be related with asyn/sync callback problem. But I don't have any idea to resolve this.
How can we resolve this?
This issue has been resolved while I clear the parameters before adding a new parameters in CreateVersion Store Procedure
like
pars = new DynamicParameters();
Full code is
public async Task<Version> CreateVersion(Version version, List<Version> versions)
{
int retval = 0;
DataTable dtVersions = TableConversion.EnitiesToDataTable<Version>(versions);
pars = new DynamicParameters();// this line resolved the issue
pars.Add("#refVersionTable", dtVersions.AsTableValuedParameter("dbo.VersionTable"));
pars.Add("#Retval", retval, DbType.Int32, ParameterDirection.Output);
string CreateVersionSP = "dbo.SPCreateVersion";
return await ExecSproc<Version>(CreateVersionSP, pars, version).ConfigureAwait(false);
}

Use a stored procedure in entity framework

I have a model-first EF model. I just imported the first stored procedure: cpas_POIDVendorProjectDate
I imported it as a function. It has three input parameters: #ProjectID(int), #VendorID(int), and #Workdate(datetime), and returns #POID(int).
Here's the SQL code:
CREATE PROCEDURE [dbo].[cpas_POIDVendorProjectDate]
#VendorID int,
#ProjectID int,
#WorkDate datetime,
#PO_ID int OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #RowCount int;
SELECT #PO_ID = ID FROM tblPO WHERE
VendorID = #VendorID
AND ExpirationDate >= #WorkDate
AND (ProjectID IS NULL OR ProjectID = #ProjectID)
AND CapitalExpense = (
SELECT CapitalExpense FROM tblProjects WHERE ID=#ProjectID)
AND GroupCode in (1,3,5);
SET #RowCount = ##RowCount;
IF (#RowCount != 1)
SET #PO_ID = -1*#RowCount;
END
I called it in my c# program as follows:
context.cpas_POIDVendorProjectDate(
currVendorID, currProjectID, currWorkDate, currPOID);
Intellisense says my use of "context" is wrong...It's a "variable", and I'm using it as a "method".
In addition, currPOID is rejected because it's looking for a system.data.objects.OjbectParameter, not an int. Intellisense is happy with the function name and other parameters (strangely...)
What am I doing wrong here?
You can always do this if nothing else works:
using(var context = new MyDataContext())
{
using(var cmd = context.Database.Connection.CreateCommand())
{
cmd.CommandText = "cpas_POIDVendorProjectDate";
cmd.CommandType = CommandType.StoredProcedure;
//if the stored proc accepts params, here is where you pass them in
cmd.Parameters.Add(new SqlParameter("VendorId", 10));
cmd.Parameters.Add(new SqlParameter("ProjectId", 12));
cmd.Parameters.Add(new SqlParameter("WorkDate", DateTimw.Now));
var poid = (int)cmd.ExecuteScalar();
}
}
If you would like an object orientated way, then Mindless passenger has a project that allows you to call a stored proc from entity frame work like this....
using (testentities te = new testentities())
{
//-------------------------------------------------------------
// Simple stored proc
//-------------------------------------------------------------
var parms1 = new testone() { inparm = "abcd" };
var results1 = te.CallStoredProc<testone>(te.testoneproc, parms1);
var r1 = results1.ToList<TestOneResultSet>();
}
... and I am working on a stored procedure framework (here) which you can call like in one of my test methods shown below...
[TestClass]
public class TenantDataBasedTests : BaseIntegrationTest
{
[TestMethod]
public void GetTenantForName_ReturnsOneRecord()
{
// ARRANGE
const int expectedCount = 1;
const string expectedName = "Me";
// Build the paraemeters object
var parameters = new GetTenantForTenantNameParameters
{
TenantName = expectedName
};
// get an instance of the stored procedure passing the parameters
var procedure = new GetTenantForTenantNameProcedure(parameters);
// Initialise the procedure name and schema from procedure attributes
procedure.InitializeFromAttributes();
// Add some tenants to context so we have something for the procedure to return!
AddTenentsToContext(Context);
// ACT
// Get the results by calling the stored procedure from the context extention method
var results = Context.ExecuteStoredProcedure(procedure);
// ASSERT
Assert.AreEqual(expectedCount, results.Count);
}
}
internal class GetTenantForTenantNameParameters
{
[Name("TenantName")]
[Size(100)]
[ParameterDbType(SqlDbType.VarChar)]
public string TenantName { get; set; }
}
[Schema("app")]
[Name("Tenant_GetForTenantName")]
internal class GetTenantForTenantNameProcedure
: StoredProcedureBase<TenantResultRow, GetTenantForTenantNameParameters>
{
public GetTenantForTenantNameProcedure(
GetTenantForTenantNameParameters parameters)
: base(parameters)
{
}
}
If either of those two approaches are any good?

Categories