The manual says that the ExecuteScalar method should be used like:
public T ExecuteScalar<T>(
string commandText,
CommandType commandType,
params DbParameter[] parameters
)
But how do I create that array of parameters? I need to provide my stored procedure 2 parameters.
DbParameter is an abstract class.
Since the type T can not be inferred from the usage, you have to specify it.
Althought you can just pass a variable number of parameters without creating the array, if you are dynamically creating a variable number of parameters, the array is your friend.
var parameters = new[]{
new SqlParameter(){ ParameterName="foo", Value="hello" },
new SqlParameter(){ ParameterName="bar", Value="World" }
};
x.ExecuteScalar<int>(commandText, commandType, parameters);
The parameters parameter has the params keyword. This means that you don't have to create the array explicitly but can pass a variable number of arguments to the method:
x.ExecuteScalar(commandText, commandType, parameter1, parameter2);
However, if you want, you can create the array explictly and pass it to the method as follows:
DbParameter[] parameters = new DbParameter[] { parameter1, parameter2 };
x.ExecuteScalar(commandText, commandType, parameters);
DbParameter is an abstract class - but you can instantiate the derived type.
If you are using Sql server it is SqlParameter:
DbParameter[] parameters = new DbParameter[2];
parameters[0] = new SqlParameter("param1", 123456);
parameters[1] = new SqlParameter("param2", "abcdef");
The params keyword means that you can specify a varying number of parameters (so from 1 to [pretty much] infinity).
You can just call the method like this:
ExecuteScalar<SomeType>("Command!", CommandType.SomeCommandType, dbParameter1, dbParameter2);
In a "perfect world" you should create any single parameter by this code:
DbProviderFactory f = DbProviderFactories.GetFactory("System.Data.SqlClient");
DbParameter parameter = f.CreateParameter();
but you probably have to use some specific platform functionality that SqlParameter implements... as SqlCommand does: SqlCommand.Parameters.AddWithValue() etc.
Related
public int ExcuteStoreProc(string query, SqlParameter[] Parameters)
{
return _context.Database.ExecuteSqlCommand(query, Parameters);
}
Above attached is my method in my class, now I want to call this method in my services class. So whenever I call it this way:
_colDeptAccessRepository.ExcuteStoreProc("CloneDeptPermissions",
new []
{
new SqlParameter("newDepartment",dept.ID),
new SqlParameter("oldDepartment",deptDto.DeptId),
});
It throws error saying : The SqlParameterCollection only accepts non-null SqlParameter type objects, not SqlParameter objects. Please help me how can I execute a method call.
You need to use Microsoft.Data.SqlClient iso System.Data.SqlClient.
So add this using statement:
using Microsoft.Data.SqlClient;
I've found lots of info on passing parameters to methods and found that having the method use params Object[] args to be a great (and sometimes only) way to do this.
i.e. sample method:
public void someMethod(params object[] args)
{
//do something
}
But when passing the parameter object like this to a Com Method, it doesn't work:
object[] passParameters = new object[2];
passParameters[0] = "test1";
passParameters[1] = "test2";
//Calling method compiled with with CodeDom
MethodInfo method = classObject.GetType().GetMethod("someMethod");
object dynamicOutput = method.Invoke(classObject, passParameters);
For it to work, I have found that I need to pass the parameter object as a new Object:
object dynamicOutput = method.Invoke(classObject,(new object[] { passParameters });
Can anyone please explain to me why I need to do this and what the reasoning is behind it? Thanks.
They don't. You can just use them as
someMethod("test1", "test2", ... )
BUT. If you're creating an array beforehand, it has to be passed differently, as you encountered
I've come to realise that the original passParameters object in the example contains two elements, as we know:
passParameters[0] = "test1";
passParameters[1] = "test2";
However, when the array is passed to the Com method, it requires being passed as so:
new object[] { passParameters })
My understanding of this is, that it creates a new object array which has put the existing 'passParameters' array into its first element [0] (effectively creating a new 2d array):
passParameters[0][0] = "test1";
passParameters[0][1] = "test2";
The method MethodInfo class in my question, required a single object passed to the Invoke method, which meant that the original 'passParameters' object with its two elements had too many arguments. By passing it inside a new object, this passes all of the elements as a single object, but containing an array of those parameters.
I have the following code which I got from the internet. I am not sure about the use of IConvertible. MSDN says:
"This interface provides methods to convert the value of an instance of an implementing type to a common language runtime type that has an equivalent value."
Is it meant that, I can assign anything to IConvertible and while using it converts it to the implementing type?
For example, in below example I pass parameters as key-value pair where key is string and value is IConvertible.
SqlParameter object = new SqlParameter(param.Key, param.Value)
SqlParameter object takes key as string and value as depending on the type declared in the table or stored procedure. How does it exactly works here?
private SqlCommand GetCommand(string procedureName,
IEnumerable<KeyValuePair<string, IConvertible>> parameters)
{
foreach (var param in parameters)
{
command.Parameters.Add(new SqlParameter(param.Key, param.Value));
}
}
I am trying to invoke a generic methods that accepts a single params parameter through reflection.
When I picked it to be non generic passing an object[] item seemed to be sufficient but when I reqired to call a generic method it does not work anymore.
var type = typeof (ClassWithGenericMethod);
var method = type.GetMethod("GenericMethod", BindingFlags.Instance | BindingFlags.Public);
var genericMethod = method.MakeGenericMethod(typeof(object));
var result = (bool)genericMethod.Invoke(new ClassWithGenericMethod(), new object[]{"param"});
Assert.IsTrue(result);
The called class:
public class ClassWithGenericMethod
{
public bool GenericMethod<T>(params string[] input)
{
return input.Length == 1;
}
}
The code fails before the assert with the following exception:
Object of type 'System.String' cannot be converted to type
'System.String[]'.
Try repleace new object[]{"param"} with new object[] { new[] { "param" } }.
Here new object[] is the array of parameters, and the first parameter should be a string[], but in your code, you use a string, hence the exception.
When using reflection to call a method that has a params keyword specified, you should just ignore the params keyword. You will need to specify an array of the appropriate type and pass that as the argument.
In your case, instead of passing a single parameter of "param", you need to pass an array of string containing a single item.
The params keyword actually affects how the method caller is compiled. When the compiler sees that the called method specifies "params", it builds the appropriate type of array and passes it. The compiler "magically" turns the following
string result = string.Concat("A","B","C","D");
into basically a compiled version of the following
string[] x = {"A", "B", "C", "D"};
string result = string.Concat(x);
Given an array of values, I would like to create an anonymous object with properties based on these values. The property names would be simply "pN" where N is the index of the value in the array.
For example, given
object[] values = { 123, "foo" };
I would like to create the anonymous object
new { p0 = 123, p1 = "foo" };
The only way I can think of to do this would be to to use a switch or if chain up to a reasonable number of parameters to support, but I was wondering if there was a more elegant way to do this:
object[] parameterValues = new object[] { 123, "foo" };
dynamic values = null;
switch (parameterValues.Length)
{
case 1:
values = new { p0 = parameterValues[0] };
break;
case 2:
values = new { p0 = parameterValues[0], p1 = parameterValues[1] };
break;
// etc. up to a reasonable # of parameters
}
Background
I have an existing set of methods that execute sql statements against a database. The methods typically take a string for the sql statement and a params object[] for the parameters, if any. The understanding is that if the query uses parameters, they will be named #p0, #p1, #p2, etc..
Example:
public int ExecuteNonQuery(string commandText, CommandType commandType, params object[] parameterValues) { .... }
which would be called like this:
db.ExecuteNonQuery("insert into MyTable(Col1, Col2) values (#p0, #p1)", CommandType.Text, 123, "foo");
Now I would like to use Dapper within this class to wrap and expose Dapper's Query<T> method, and do so in a way that would be consistent with the existing methods, e.g. something like:
public IEnumerable<T> ExecuteQuery<T>(string commandText, CommandType commandType, params object[] parameterValues) { .... }
but Dapper's Query<T> method takes the parameter values in an anonymous object:
var dog = connection.Query<Dog>("select Age = #Age, Id = #Id", new { Age = (int?)null, Id = guid });
leading to my question about creating the anonymous object to pass parameters to Dapper.
Adding code using the DynamicParameter class as requested by #Paolo Tedesco.
string sql = "select * from Account where Id = #p0 and username = #p1";
dynamic values = new DynamicParameter(123, "test");
var accounts = SqlMapper.Query<Account>(connection, sql, values);
throws an exception at line 581 of Dapper's SqlMapper.cs file:
using (var reader = cmd.ExecuteReader())
and the exception is a SqlException:
Must declare the scalar variable "#p0".
and checking the cmd.Parameters property show no parameters configured for the command.
You are misusing Dapper, you should never need to do this, instead either implement IDynamicParameters or use the specific extremely flexible DynamicParameters class.
In particular:
string sql = "select * from Account where Id = #id and username = #name";
var values = new DynamicParameters();
values.Add("id", 1);
values.Add("name", "bob");
var accounts = SqlMapper.Query<Account>(connection, sql, values);
DynamicParameters can take in an anonymous class in the constructor. You can concat DynamicParameters using the AddDynamicParams method.
Further more, there is no strict dependency on anon-types. Dapper will allow for concrete types as params eg:
class Stuff
{
public int Thing { get; set; }
}
...
cnn.Execute("select #Thing", new Stuff{Thing = 1});
Kevin had a similar question: Looking for a fast and easy way to coalesce all properties on a POCO - DynamicParameters works perfectly here as well without any need for magic hoop jumping.
Not exactly an anonymous object, but what about implementing a DynamicObject which returns values for p1 ... pn based on the values in the array? Would that work with Dapper?
Example:
using System;
using System.Dynamic;
using System.Text.RegularExpressions;
class DynamicParameter : DynamicObject {
object[] _p;
public DynamicParameter(params object[] p) {
_p = p;
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
Match m = Regex.Match(binder.Name, #"^p(\d+)$");
if (m.Success) {
int index = int.Parse(m.Groups[1].Value);
if (index < _p.Length) {
result = _p[index];
return true;
}
}
return base.TryGetMember(binder, out result);
}
}
class Program {
static void Main(string[] args) {
dynamic d1 = new DynamicParameter(123, "test");
Console.WriteLine(d1.p0);
Console.WriteLine(d1.p1);
}
}
You cannot dynamically create anonymous objects. But Dapper should work with dynamic object. For creating the dynamic objects in a nice way, you could use Clay. It enables you to write code like
var person = New.Person();
person["FirstName"] = "Louis";
// person.FirstName now returns "Louis"