I am missing something obvious here, and I think I need a new perspective.
I am passing a parameter of type object[]. I want to extract the element within the array and assign its value to another variable, as I'm looping through another list:
private void PopulateValues(List<SqlParameters> parameters, object[] parameterValues) {
int index = 0;
foreach(var parameter in parameters) {
parameter.Value = parameterValues[index];
index++;
}
Try as I might, the parameter.Value is set to the object array rather than the object's value at position i.
What am I missing here? Is there a different way to get at the object value inside an object array other than by index/position?
Okay .. here's exactly what I'm doing.
protected SqlCommand CreateStoredProcedureCommand(string storedProcedureName, object[] parameterValues)
{
SqlCommand cmd = new SqlCommand(storedProcedureName, Connection)
{CommandTimeout = _commandTimeout, CommandType = CommandType.StoredProcedure};
SqlCommandBuilder.DeriveParameters(cmd);
int index = 0;
foreach(SqlParameter parameter in cmd.Parameters)
{
if (parameter.Direction == ParameterDirection.Input || parameter.Direction == ParameterDirection.InputOutput)
{
parameter.Value = parameterValues[index]
index++;
}
}
return cmd;
}
It seems like this should work, so I think I'll go back and investigate what I'm actually passing. Maybe I'm passing an array of arrays or doing something else weird.
Found it. I was doing something weird.
I was sending the parameterValues as a list this way:
CreateStoredProcedureCommand("spName", pContext.SqlParameters.Select(b=>b.Value).ToList())
Changed it to ToArray() and it worked.
Related
In our datalayer, I want to discourage the use of string concatenation and make people use parameters instead.
But as far as I know, there is no way to see if a parameter is a concatenated string.
I have no code example to show of detection, since I know of none.
var result = db.executeQuery("SELECT * FROM table WHERE id = " + id);
This is the kind of code I'd like to get rid of, either to replace with something like:
db.executeQuery($"SELECT * FROM table WHERE id = {id}");
or
db.executeQuery("SELECT * FROM table WHERE id = {0}", id);
Edit:
The command executeQuery is in our datalayer and handles parameters as SqlParameters, with types and values.
So in this case a SqlParameter called #id with type int would be created.
Regarding the FormattableString:
public T ExecuteObject<T>(FormattableString sql)
{
return executeSingleRow(sql.Format, sql.GetArguments()).ToType<T>();
}
Regarding the ExecuteQuery:
public int executeNonQuery(string sql, params object[] parameters)
{
var traceI = Traceadd(sql, parameters);
if (!open())
throw new Exception("Error executing query!", lastException);
try
{
command = Connection.CreateCommand();
command.CommandText = sql;
sql.SQLFormat(ref command, parameters);
var res = command.ExecuteNonQuery();
command.Parameters.Clear();
if (traceI != null)
traceI.Stop();
return res;
}
catch (Exception ex)
{
if (traceI != null)
traceI.Stop();
throw new DBException(command.CommandText, command.Parameters, ex);
}
}
If your executeQuery method only has a parameter of FormattableString, then you should be fine already - there's no conversion from string to FormattableString. For example:
using System;
class Program
{
static void Main(string[] args)
{
int id = 10;
ExecuteQuery("SELECT * FROM table WHERE id = " + id);
}
static void ExecuteQuery(FormattableString query)
{
}
}
That gives an error:
Test.cs(8,22): error CS1503: Argument 1: cannot convert from 'string' to 'System.FormattableString'
You just need to make sure that you don't have an overload of your method accepting string. The result of string concatenation is never a FormattableString. Indeed, I would strongly advise that you avoid ever overloading a method to accept FormattableString and string... there's no point in doing so if you're not going to change the behaviour, and if you are going to change the behaviour, that could be really confusing.
I'd personally consider changing to use an extension method on FormattableString though - something like:
public static SqlCommand ToCommand(
this FormattableString query,
SqlConnection connection)
{
// ...
}
That way you can separate the command creation from the execution... which means (aside from anything else) that you can then call ExecuteReader or ExecuteNonQuery without having any extra code yourself.
I have a project where I have been given a stored procedure that I must use. It is used to create a new record in the database. It returns an Int32 which is the new ID of the record in the table. It also has a temp table where errors are stored and sent back in a second result. So basically the last two lines are
SELECT #NewID
SELECT * From #ErrorsTable
I have to use Entity Framework to call the stored procedure. My problem I can't figure out how to get both return results.
The default When updating from the Database is to just return an Int32. I tried following some examples where they do multiple return results from a stored procedure but I think its not working for me because all the examples return multiple complex types, and I have a mix of a scalar and complex type.
So I guess I ended up solving it by mostly following the code code only option at this link http://msdn.microsoft.com/en-us/data/jj691402.aspx
I added a new method to my Repository that returned called a stored procedure and returned multiple results sets. Something along these lines
Public Object[] StoredProcCall(string storedProc, SqlDbParams sqlParams, type[] types)
{
var cmd = dbContext.Database.Connection.CreateCommand();
cmd.CommandText = storedProc;
cmd.CommandType = CommandType.StoredProcedure;
if(parameters != null)
cmd.Parameters.AddRange(sqlParams);
var reader = cmd.ExecuteReader();
try
{
dbContext.Database.Connection.Open();
object[] mObj = new object[types.Count];
for(int i = 0; i < types.Count; i++)
{
System.Reflection.MethodInfo method = typeof(AutoMapper.Mapper).GetMethod("DynamicMap", new Type[] { typeof(object)});
var generic = method.MakeGenericMethod(types[i]);
objected mappedData = generic.Invoke(this, new object[] { reader});
mObj[i] = mappedData;
if(!reader.NextResult())
break;
}
return mObj;
}
finally
{
dbContext.Database.Connection.Close();
}
}
The only think is that for that to work I had to make a class that that consist of only one property which is my simple type. and every type in the array needs to be IEnumerable which is ok in my case. But That is the basics of what I did and can be changed as needed.
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"
I have a stored procedure. One of its input parameters is expecting a char(8). I try to convert a string "AAA" to this particular parameter type, which is a DBType.AnsiStringFixedLength.
object v = Convert.ChangeType("AAA", param.DbType.GetTypeCode());
// param is AnsiStringFixedLength
However, all I get is an exception: Input string was not in a correct format.
And the stack trace says: at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) [...]
Why is System.Convert trying to convert a string into a number, even though the prodecure's parameter is expecting a char(8)? How do I solve this? I don't want to use one huge switch case mapping all SQL types to CLR types...
EDIT:
This is the code in question: (A generic method to call any MS SQL stored procedure)
using (SqlConnection conn = new SqlConnection(this.config.ConnectionString))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = this.config.StoredProcedureName;
conn.Open();
SqlCommandBuilder.DeriveParameters(cmd);
foreach (SqlParameter param in cmd.Parameters)
{
if (param.Direction == ParameterDirection.Input ||
param.Direction == ParameterDirection.InputOutput)
{
try
{
string rawParam = param.ParameterName.Replace("#", "");
if (this.config.Parameters.ContainsKey(rawParam))
{
try
{
param.Value = Convert.ChangeType(this.config.Parameters[rawParam],
param.DbType.GetTypeCode());
}
catch(Exception oops)
{
throw new Exception(string.Format("Could not convert to '{0}'.", param.DbType), oops);
}
}
else
throw new ArgumentException("parameter's not available");
}
catch (Exception e)
{
throw;
}
}
}
cmd.ExecuteNonQuery();
}
}
The actual parameter values are provided by this.config.Parameters - all of them are strings. I iterate through SqlCommand's parameter list and set them accordingly. Converting the string values to the parameter's Sql type is necessary here, and as far as I can see, the Sql type is provided by param.DBType.
You seem to mix up some things here, or I don't get what you try to do. The DbType (an enumeration) inherits Enum and that implements IConvertible -> You can call GetTypeCode(). But - you are now calling Enum.GetTypeCode(), which returns the underlying type. If you didn't specify it (and DbType didn't) any Enum is backed by an int.
What are you trying to solve with the code anyway? Why would you want to change the type of a string if the parameter is a string (although with a fixed length)?
Looking at the question some more it seems even more odd. You have an object v (probably for value?) - what do you care about the type?
object v1 = "Foo";
object v1 = 42;
What is the difference for you? I guess you want to pass the values to something else, but - if you only reference the value as object you might still need to cast it.
Please update your question and explain what you really want to do, what you expect to gain.
Regarding the comment:
I'm using Convert.ChangeType(object
value, TypeCode typeCode), so it's not
really converting into an Enum/int. At
least that's what I thought...
See above: DbType.GetTypeCode() is not what you want. Try it, give me the benefit of the doubt: What do you expect to get from DbType.AnsiStringFixedLength.GetTypeCode()? What is the actual result, if you try it?
Now to your code: You try to set the SqlParameter.Value property to the "correct" type. Two things: According to the documentation you probably want to set the SqlParameter.SqlValue, which is the value using SQL types according to the docs. SqlParameter.Value, on the other hand, is the value using CLR types and allows to infer both DbType and SqlValue. Sidenote, implementation detail: The SqlParameter.SqlValue setter just calls the setter of SqlParameter.Value again...
I would expect that the ADO.NET stuff converts the value on its own, if at all possible. What error are you getting without jumping through this hoops?
I have created a function that takes a SQL command and produces output that can then be used to fill a List of class instances. The code works great. I've included a slightly simplified version without exception handling here just for reference - skip this code if you want to jump right the problem. If you have suggestions here, though, I'm all ears.
public List<T> ReturnList<T>() where T : new()
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
Type objectType = typeof (T);
FieldInfo[] typeFields = objectType.GetFields();
while (nwReader.Read())
{
T obj = new T();
foreach (FieldInfo info in typeFields)
{
for (int i = 0; i < nwReader.FieldCount; i++)
{
if (info.Name == nwReader.GetName(i))
{
info.SetValue(obj, nwReader[i]);
break;
}
}
}
fdList.Add(obj);
}
nwReader.Close();
return fdList;
}
As I say, this works just fine. However, I'd like to be able to call a similar function with an anonymous class for obvious reasons.
Question #1: it appears that I must construct an anonymous class instance in my call to my anonymous version of this function - is this right? An example call is:
.ReturnList(new { ClientID = 1, FirstName = "", LastName = "", Birthdate = DateTime.Today });
Question #2: the anonymous version of my ReturnList function is below. Can anyone tell me why the call to info.SetValue simply does nothing? It doesn't return an error or anything but neither does it change the value of the target field.
public List<T> ReturnList<T>(T sample)
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
// Cannot use FieldInfo[] on the type - it finds no fields.
var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read())
{
// No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (PropertyDescriptor info in properties)
{
for (int i = 0; i < nwReader.FieldCount; i++)
{
if (info.Name == nwReader.GetName(i))
{
// This loop runs fine but there is no change to obj!!
info.SetValue(obj, nwReader[i]);
break;
}
}
}
fdList.Add(obj);
}
nwReader.Close();
return fdList;
}
Any ideas?
Note: when I tried to use the FieldInfo array as I did in the function above, the typeFields array had zero elements (even though the objectType shows the field names - strange). Thus, I use TypeDescriptor.GetProperties instead.
Any other tips and guidance on the use of reflection or anonymous classes are appropriate here - I'm relatively new to this specific nook of the C# language.
UPDATE: I have to thank Jason for the key to solving this. Below is the revised code that will create a list of anonymous class instances, filling the fields of each instance from a query.
public List<T> ReturnList<T>(T sample)
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read())
{
int objIdx = 0;
object[] objArray = new object[properties.Count];
foreach (PropertyDescriptor info in properties)
objArray[objIdx++] = nwReader[info.Name];
fdList.Add((T)Activator.CreateInstance(sample.GetType(), objArray));
}
nwReader.Close();
return fdList;
}
Note that the query has been constructed and the parameters initialized in previous calls to this object's methods. The original code had an inner/outer loop combination so that the user could have fields in their anonymous class that didn't match a field. However, in order to simplify the design, I've decided not to permit this and have instead adopted the db field access recommended by Jason. Also, thanks to Dave Markle as well for helping me understand more about the tradeoffs in using Activator.CreateObject() versus GenUninitializedObject.
Anonymous types encapsulate a set of read-only properties. This explains
Why Type.GetFields returns an empty array when called on your anonymous type: anonymous types do not have public fields.
The public properties on an anonymous type are read-only and can not have their value set by a call to PropertyInfo.SetValue. If you call PropertyInfo.GetSetMethod on a property in an anonymous type, you will receive back null.
In fact, if you change
var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read()) {
// No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (PropertyDescriptor info in properties) {
for (int i = 0; i < nwReader.FieldCount; i++) {
if (info.Name == nwReader.GetName(i)) {
// This loop runs fine but there is no change to obj!!
info.SetValue(obj, nwReader[i]);
break;
}
}
}
fdList.Add(obj);
}
to
PropertyInfo[] properties = sample.GetType().GetProperties();
while (nwReader.Read()) {
// No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (PropertyInfo info in properties) {
for (int i = 0; i < nwReader.FieldCount; i++) {
if (info.Name == nwReader.GetName(i)) {
// This loop will throw an exception as PropertyInfo.GetSetMethod fails
info.SetValue(obj, nwReader[i], null);
break;
}
}
}
fdList.Add(obj);
}
you will receive an exception informing you that the property set method can not be found.
Now, to solve your problem, what you can do is use Activator.CreateInstance. I'm sorry that I'm too lazy to type out the code for you, but the following will demonstrate how to use it.
var car = new { Make = "Honda", Model = "Civic", Year = 2008 };
var anothercar = Activator.CreateInstance(car.GetType(), new object[] { "Ford", "Focus", 2005 });
So just run through a loop, as you've done, to fill up the object array that you need to pass to Activator.CreateInstance and then call Activator.CreateInstance when the loop is done. Property order is important here as two anonymous types are the same if and only if they have the same number of properties with the same type and same name in the same order.
For more, see the MSDN page on anonymous types.
Lastly, and this is really an aside and not germane to your question, but the following code
foreach (PropertyDescriptor info in properties) {
for (int i = 0; i < nwReader.FieldCount; i++) {
if (info.Name == nwReader.GetName(i)) {
// This loop runs fine but there is no change to obj!!
info.SetValue(obj, nwReader[i]);
break;
}
}
}
could be simplified by
foreach (PropertyDescriptor info in properties) {
info.SetValue(obj, nwReader[info.Name]);
}
I had the same problem, I resolved it by creating a new Linq.Expression that's going to do the real job and compiling it into a lambda: here's my code for example:
I want to transform that call:
var customers = query.ToList(r => new
{
Id = r.Get<int>("Id"),
Name = r.Get<string>("Name"),
Age = r.Get<int>("Age"),
BirthDate = r.Get<DateTime?>("BirthDate"),
Bio = r.Get<string>("Bio"),
AccountBalance = r.Get<decimal?>("AccountBalance"),
});
to that call:
var customers = query.ToList(() => new
{
Id = default(int),
Name = default(string),
Age = default(int),
BirthDate = default(DateTime?),
Bio = default(string),
AccountBalance = default(decimal?)
});
and do the DataReader.Get things from the new method, the first method is:
public List<T> ToList<T>(FluentSelectQuery query, Func<IDataReader, T> mapper)
{
return ToList<T>(mapper, query.ToString(), query.Parameters);
}
I had to build an expression in the new method:
public List<T> ToList<T>(Expression<Func<T>> type, string sql, params object[] parameters)
{
var expression = (NewExpression)type.Body;
var constructor = expression.Constructor;
var members = expression.Members.ToList();
var dataReaderParam = Expression.Parameter(typeof(IDataReader));
var arguments = members.Select(member =>
{
var memberName = Expression.Constant(member.Name);
return Expression.Call(typeof(Utilities),
"Get",
new Type[] { ((PropertyInfo)member).PropertyType },
dataReaderParam, memberName);
}
).ToArray();
var body = Expression.New(constructor, arguments);
var mapper = Expression.Lambda<Func<IDataReader, T>>(body, dataReaderParam);
return ToList<T>(mapper.Compile(), sql, parameters);
}
Doing this that way, i can completely avoid the Activator.CreateInstance or the FormatterServices.GetUninitializedObject stuff, I bet it's a lot faster ;)
Question #2:
I don't really know, but I would tend to use Activator.CreateObject() instead of FormatterServices.GetUninitializedObject(), because your object might not be created properly. GetUninitializedObject() won't run a default constructor like CreateObject() will, and you don't necessarily know what's in the black box of T...
This method stores one line of a sql query in a variable of anonymous type. You have to pass a prototype to the method. If any property of the anonymous type can not be found within the sql query, it is filled with the prototype-value. C# creates constructors for its anonymous classes, the parameters have the same names as the (read-only) properties.
public static T GetValuesAs<T>(this SqlDataReader Reader, T prototype)
{
System.Reflection.ConstructorInfo constructor = prototype.GetType().GetConstructors()[0];
object[] paramValues = constructor.GetParameters().Select(
p => { try { return Reader[p.Name]; }
catch (Exception) { return prototype.GetType().GetProperty(p.Name).GetValue(prototype); } }
).ToArray();
return (T)prototype.GetType().GetConstructors()[0].Invoke(paramValues);
}