I am currently developing an application using C# (Visual Studio 2019)and SQL Server 2017 using Dapper. Below is a routine that works fine right now to execute a stored procedure in SQL Server.
The C# code uses the MVVM framework, and I currently have 60 model classes to map the tables in the SQL Server database. There are about 120 stored procedures that produce results that are mapped to the model classes using Dapper.
Consider the following code snippet as pseudocode (but it actually works to execute the stored procedure and return the correct results).
public List<SomeDefinedModel> ExecuteSQLSPROC(string SPROCName, SqlParameter[] parameters)
{
using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(DBHelper.CNNVal("MYLocalServer")))
{
System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand();
command.Connection = (SqlConnection)connection;
command.CommandText = SPROCName;
DynamicParameters parms = new DynamicParameters();
for (int i = 0; i < parameters.Length; i++)
{
var parmname = parameters[i].ParameterName;
var parmvalue = parameters[i].Value;
parms.Add(parmname, parmvalue);
}
var output = connection.Query<SomeDefinedModel>(SPROCName, parms, commandType: CommandType.StoredProcedure).ToList();
return output;
}
}
I want to change this routine so the routine so that it method signature is as follows:
public List<TheNameOfTheModelToMap> ExecuteSQLSPROC(string SPROCName, SqlParameter[] parameters, TheNameOfTheModelToMap)
That is, the designation of the model to which Dapper is to map is a variable - it can be any valid model. I have define the variable TheNameOfTheModelToMap as type object, and the return value as var, as List<object>, and so forth. They produce errors because of this syntax:
var output = connection.Query<TheNameOfTheModelToMap>(SPROCName, parms, commandType: CommandType.StoredProcedure).ToList();
I want to comply with the principles of DRY and code just one method to accept ANY model designation, ANY stored procedure designation and have Dapper map the query results. Any possibilities?
I think you just need one generic method like so:
public List<T> ExecuteSQLSPROC<T>(string SPROCName, SqlParameter[] parameters)
{
using (var connection = new System.Data.SqlClient.SqlConnection(DBHelper.CNNVal("MYLocalServer")))
{
var parms = new DynamicParameters();
foreach (var parameter in parameters)
parms.Add(parameter.ParameterName, parameter.Value);
var output = connection.Query<T>(
SPROCName,
parms,
commandType: CommandType.StoredProcedure
)
.ToList();
return output;
}
}
And every other method becomes a specialized invoke of it:
public List<SomeDefinedModel> ExecuteOneProc(SqlParameter[] parameters)
=> ExecuteSQLSPROC<SomeDefinedModel>("OneProcName", parameters);
Is this ok for your use case?
If you need one procedure for single values (which would not use Dapper's Query method) you would just create specialized variations of the ExecuteSQLSPROC like ExecuteStoredProcSingleRow or something along those lines.
Related
I need to call a sql stored procedure which have an input parameter with type "IntList" that is a user-defined table type,I wanna set this parameter with a list of int through dapper dynamic parameter, in this case I don't have Structured dbtype, how can I handle it? I've tried this :
using var connection = _dbConnectionFactory.GetDefaultServerConnectionString();
var parameters = new DynamicParameters();
parameters.Add("#RequestId", query.RequestId, DbType.DateTime);
parameters.Add("#Agencies", query.AgencyIds);
return await connection.QueryAsync<TerminalDto>(
"SelectTerminals", parameters,
commandType: CommandType.StoredProcedure);
but it raise "too many arguments" error!
I'm posting this for two reasons
I'm new to PostgreSQL and it took a while to piece this information together, so I thought someone else would find this helpful and
to ask if there is another way to call a PostgreSQL stored procedure that doesn't require all of the parameters to be included in the stored procedure name/signature.
The following uses Dapper and Npgsql to make a call to a PostgreSQL stored procedure that inserts (null id_inout passed in) or updates a record (id_inout has a value).
I'd like to understand why PostgreSQL requires the entire stored procedure signature when making the call.
public static int? PO_Save(PurchaseOrder po)
{
int? recordId = null;
using (var cn = new NpgsqlConnection(AppSettings.ConnectionString))
{
if (cn.State != ConnectionState.Open)
cn.Open();
var procName = "CALL po_save(#in_ponumber,#in_deliverydate,#in_bldnum," +
"#in_facname,#in_facnumber,#in_facaddress1,#in_facaddress2,#in_city," +
"#in_state,#in_zip,#in_theme,#id_inout)";
var p = new Dapper.DynamicParameters();
p.Add("#in_ponumber", po.PONumber);
p.Add("#in_deliverydate", po.DeliveryDate);
p.Add("#in_bldnum", po.BldNum);
p.Add("#in_facname", po.FacName);
p.Add("#in_facnumber", po.FacNumber);
p.Add("#in_facaddress1", po.FacAddress1);
p.Add("#in_facaddress2", po.FacAddress2);
p.Add("#in_city", po.City);
p.Add("#in_state", po.State);
p.Add("#in_zip", po.Zip);
p.Add("#in_theme", po.Theme);
p.Add("#id_out", po.POID, null, ParameterDirection.InputOutput);
var res = cn.Execute(procName, p);
recordId = p.Get<int>("#id_inout");
}
return recordId;
}
You should be able to pass commandType: CommandType.StoredProcedure to Execute, e.g:
var res = cn.Execute(
"po_save",
new {
in_ponumber = po.PONumber,
in_deliverydate = po.DeliveryDate,
// etc...
},
commandType: CommandType.StoredProcedure,
);
Here's the docs with such an example: https://github.com/StackExchange/Dapper/blob/main/Readme.md#stored-procedures
I wanted to find an answer to this myself, and beginning with Npgsql 7.0, CommandType.StoredProcedure will now invoke stored procedures and not functions as before.
I am trying to query a set of Azure SQL Databases using the sp_execute_remote SQL function with Dapper and DynamicParameters as follows:
Task<List<T>> ExecuteMultiShardCommand<T>(SqlBuilder.Template Template, int Timeout = 120)
{
var mParams = new DynamicParameters(Template.Parameters);
mParams.Add("#datasourcename", "<datasource>");
mParams.Add("#statement", Template.RawSql, DbType.String);
using (var conn = new SqlConnection(config["<sqlconnectionstring>"]))
{
var results = await conn.QueryAsync<T>("sp_execute_remote", mParams, commandTimeout: Timeout, commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
Running this throws a SQL exception:
System.Data.SqlClient.SqlException : Procedure expects parameter '#statement' of type 'ntext/nchar/nvarchar'.
I have not configured Dapper anywhere in my application. Isn't the default handling for DbType.String a nvarchar?
I have been using Dapper as my ORM for my .NET Core Web Api.
When using Dapper to query a stored procedure from my database with one parameter, it works exactly as expected. When I add more than one parameter, it does not return anything back to my datamodel like it should.
I suspect this has to do either with my syntax or the way I am structuring the query. The stored procedure I am using below works as expected when executed inside a SSMS query window.
Here is my method containing the Dapper Query in my DAL:
public List<Players> C_GetAllActivePlayersInSport(int orgID, int sportID)
{
using (IDbConnection db = new SqlConnection(_connectionString))
{
var returnedData = db.Query<Players>("dbo.spPlayers_GetAllActivePlayers_by_Sport #orgID, #sportID", new { orgID = orgID, sportID = sportID }).ToList();
return returnedData;
}
}
The values passed in make it to the method and query above, but after the query executes, it returns a list with a count of 0.
Any help would be greatly appreciated!
Try:
var returnedData = db.Query<Players>(
"dbo.spPlayers_GetAllActivePlayers_by_Sport",
new { orgID, sportID }, commandType: CommandType.StoredProcedure).ToList();
(note: .AsList() would be marginally preferable)
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
SQL Server & .net support calling a stored procedure with param's values wihout providing param's names?
I want to call a command.ExecuteReader() of type Stored Procedure, however I do not want the parameter names that I pass to be identical to the ones in the SP. Below is a sample of what I'm trying to do
SP:
ALTER PROCEDURE SPName
#Id nvarchar(50)
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM TableName
WHERE ColumnName = #Id
END
GO
Code:
using (SqlCommand command = new SqlCommand(spName, connection) { CommandType = CommandType.StoredProcedure })
{
command.parameters.Add(new SqlParameter(*paramaeter name*, sqlDbType.nvarchar){ Value = "SomeValue"};
}
If you want a generic style function, without needing an extra round-trip, and you're happy to use reflection you could use something like this.
// Return an array of SqlParameter's by using reflection on ParamObject
private static SqlParameter[] GetParametersFromObject( object ParamObject )
{
var Params = new List<SqlParameter>();
foreach( var PropInfo in ParamObject.GetType().GetProperties() )
{
Params.Add( new SqlParameter( PropInfo.Name, PropInfo.GetValue( ParamObject, null ) ) );
}
return Params.ToArray();
}
public static void ExecuteSP( SqlConnection Connection, string SPName, object ParamObject )
{
using( var Command = new SqlCommand() )
{
Command.Connection = Connection;
Command.CommandType = CommandType.StoredProcedure;
Command.CommandText = SPName;
Command.Parameters.AddRange( GetParametersFromObject( ParamObject ) );
// Command.ExecuteReader()...
}
}
This uses reflection to get the property names and values out of the anonymous object to populate the SqlCommand. This can be used as such;
ExecuteSP( Conn, "GetStuff", new { Id = 7, Name = "Test" } );
This way ExecuteSP is 'generic' and you pick the parameter names and values when you call ExecuteSP.
Simple fact - you ultimately have to use the correct parameter name when calling a stored procedure because SQL server binds parameters by name (even when you use EXEC to call an SP without using named parameters, the parser binds them by name from left to right).
So if you want to use a different name you will need to introduce an intermediate layer between your SqlCommand and the target SP.
But if you just want to not care about the name and have it automatically discovered - then you can use the technique mentioned by Conrad Frix in his accepted answer on SQL Server & .net support calling a stored procedure with param's values wihout providing param's names? - which is why I've marked as a duplicate, because it is ultimately what you want to do, even if the reasons are different.
For SqlServer there is a DeriveParameters method that takes a command object and queries the database for the parameters (names and types) of the requested stored procedure.
You can then iterate over them and supply values.
Note: this means an extra trip to the database, so if you need this often, you might want to cache the results.
The method below allows you to write generic code for calling stored procedures, but also gives you the flexibility to perform specific actions for each different stored procedure
public delegate void SqlCOmmandDelegate(SqlCommand command);
public class Dal
{
public void ExecuteStoredProcedure(string procedureName,
SqlCommandDelgate commandDelegate)
{
using (SqlConnection connection = new SqlConnection())
{
connection.ConnectionString = GetConnectionString();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = procedureName;
connection.Open();
commandDelegate(command);
}
}
}
}
class UsesDal
{
public CallFirstProcedure(int value)
{
string userName;
ExecuteStoredProcedure("FIRST_PROCEDURE",
delegate(SqlCommand command)
{
command.Parameters.Add("UserID", value);
command.ExecuteReader();
//Do stuff with results e.g.
username = command.Parameters.Parameters["UserName"].ToString();
}
}
public CallOtherProcedure(string value)
{
int id;
ExecuteStoredProcedure("OTHER_PROCEDURE",
delegate(SqlCommand command)
{
command.Parameters.Add("ParameterName", value);
id = command.ExecuteScalar();
}
}
}