OdbcCommand with parameters doesn't return anything - c#

I have the next code:
public static List<T> ExecuteQuery<T>(string qry, Dictionary<string, object> parameters = null)
{
using (var connection = new OdbcConnection(ConnectionString))
{
connection.Open();
var command = new OdbcCommand(qry, connection);
if (parameters != null)
{
foreach (var parameter in parameters)
{
command.Parameters.AddWithValue(parameter.Key, parameter.Value);
}
}
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
// ...
}
}
}
}
When I execute something like:
ExecuteQuery<MyClass>("SELECT * FROM MYTABLE WHERE ID = 1");
It returns all the correct data. But when I send this:
ExecuteQuery<MyClass>(
"SELECT * FROM MYTABLE WHERE ID = #Id",
new Dictionary<string, object> { { "#Id", 1 } });
doesn't return anything. I've tried with command.Parameters.Add but is the same history.
So, where is my error?
Thanks to everyone.

Try substituting the named parameter with a ?, which I believe is what OdbcCommand uses.
ExecuteQuery<MyClass>("SELECT * FROM MYTABLE WHERE ID = ?",
new Dictionary<string, object> { { "#Id", 1 } });
Also, since a Dictionary doesn't guarantee order, you may want to replace that with a List<T> in the event you have more than one parameter. A list guarantees order.
I'd also suggest specifying the OdbcType and not just adding all your parameter values as objects. It has to infer the correct data type otherwise, and it may guess wrong. You could create a more permanent class... here I decided to just use a Tuple<T,T,T>.
public static List<T> ExecuteQuery<T>(string qry, List<Tuple<string, OdbcType, object>> parameters = null)
...
...
foreach (var parameter in parameters)
{
command.Parameters.Add(parameter.Item1, parameter.Item2).Value = parameter.Item3;
}

Related

Call Oracle table of object in procedure from .net core [duplicate]

I have a SQL Server 2005 database. In a few procedures I have table parameters that I pass to a stored proc as an nvarchar (separated by commas) and internally divide into single values. I add it to the SQL command parameters list like this:
cmd.Parameters.Add("#Logins", SqlDbType.NVarchar).Value = "jim18,jenny1975,cosmo";
I have to migrate the database to SQL Server 2008. I know that there are table value parameters, and I know how to use them in stored procedures. But I don't know how to pass one to the parameters list in an SQL command.
Does anyone know correct syntax of the Parameters.Add procedure? Or is there another way to pass this parameter?
DataTable, DbDataReader, or IEnumerable<SqlDataRecord> objects can be used to populate a table-valued parameter per the MSDN article Table-Valued Parameters in SQL Server 2008 (ADO.NET).
The following example illustrates using either a DataTable or an IEnumerable<SqlDataRecord>:
SQL Code:
CREATE TABLE dbo.PageView
(
PageViewID BIGINT NOT NULL CONSTRAINT pkPageView PRIMARY KEY CLUSTERED,
PageViewCount BIGINT NOT NULL
);
CREATE TYPE dbo.PageViewTableType AS TABLE
(
PageViewID BIGINT NOT NULL
);
CREATE PROCEDURE dbo.procMergePageView
#Display dbo.PageViewTableType READONLY
AS
BEGIN
MERGE INTO dbo.PageView AS T
USING #Display AS S
ON T.PageViewID = S.PageViewID
WHEN MATCHED THEN UPDATE SET T.PageViewCount = T.PageViewCount + 1
WHEN NOT MATCHED THEN INSERT VALUES(S.PageViewID, 1);
END
C# Code:
private static void ExecuteProcedure(bool useDataTable,
string connectionString,
IEnumerable<long> ids)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "dbo.procMergePageView";
command.CommandType = CommandType.StoredProcedure;
SqlParameter parameter;
if (useDataTable) {
parameter = command.Parameters
.AddWithValue("#Display", CreateDataTable(ids));
}
else
{
parameter = command.Parameters
.AddWithValue("#Display", CreateSqlDataRecords(ids));
}
parameter.SqlDbType = SqlDbType.Structured;
parameter.TypeName = "dbo.PageViewTableType";
command.ExecuteNonQuery();
}
}
}
private static DataTable CreateDataTable(IEnumerable<long> ids)
{
DataTable table = new DataTable();
table.Columns.Add("ID", typeof(long));
foreach (long id in ids)
{
table.Rows.Add(id);
}
return table;
}
private static IEnumerable<SqlDataRecord> CreateSqlDataRecords(IEnumerable<long> ids)
{
SqlMetaData[] metaData = new SqlMetaData[1];
metaData[0] = new SqlMetaData("ID", SqlDbType.BigInt);
SqlDataRecord record = new SqlDataRecord(metaData);
foreach (long id in ids)
{
record.SetInt64(0, id);
yield return record;
}
}
Further to Ryan's answer you will also need to set the DataColumn's Ordinal property if you are dealing with a table-valued parameter with multiple columns whose ordinals are not in alphabetical order.
As an example, if you have the following table value that is used as a parameter in SQL:
CREATE TYPE NodeFilter AS TABLE (
ID int not null
Code nvarchar(10) not null,
);
You would need to order your columns as such in C#:
table.Columns["ID"].SetOrdinal(0);
// this also bumps Code to ordinal of 1
// if you have more than 2 cols then you would need to set more ordinals
If you fail to do this you will get a parse error, failed to convert nvarchar to int.
Generic
public static DataTable ToTableValuedParameter<T, TProperty>(this IEnumerable<T> list, Func<T, TProperty> selector)
{
var tbl = new DataTable();
tbl.Columns.Add("Id", typeof(T));
foreach (var item in list)
{
tbl.Rows.Add(selector.Invoke(item));
}
return tbl;
}
The cleanest way to work with it. Assuming your table is a list of integers called "dbo.tvp_Int" (Customize for your own table type)
Create this extension method...
public static void AddWithValue_Tvp_Int(this SqlParameterCollection paramCollection, string parameterName, List<int> data)
{
if(paramCollection != null)
{
var p = paramCollection.Add(parameterName, SqlDbType.Structured);
p.TypeName = "dbo.tvp_Int";
DataTable _dt = new DataTable() {Columns = {"Value"}};
data.ForEach(value => _dt.Rows.Add(value));
p.Value = _dt;
}
}
Now you can add a table valued parameter in one line anywhere simply by doing this:
cmd.Parameters.AddWithValueFor_Tvp_Int("#IDValues", listOfIds);
Use this code to create suitable parameter from your type:
private SqlParameter GenerateTypedParameter(string name, object typedParameter)
{
DataTable dt = new DataTable();
var properties = typedParameter.GetType().GetProperties().ToList();
properties.ForEach(p =>
{
dt.Columns.Add(p.Name, Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType);
});
var row = dt.NewRow();
properties.ForEach(p => { row[p.Name] = (p.GetValue(typedParameter) ?? DBNull.Value); });
dt.Rows.Add(row);
return new SqlParameter
{
Direction = ParameterDirection.Input,
ParameterName = name,
Value = dt,
SqlDbType = SqlDbType.Structured
};
}
If you have a table-valued function with parameters, for example of this type:
CREATE FUNCTION [dbo].[MyFunc](#PRM1 int, #PRM2 int)
RETURNS TABLE
AS
RETURN
(
SELECT * FROM MyTable t
where t.column1 = #PRM1
and t.column2 = #PRM2
)
And you call it this way:
select * from MyFunc(1,1).
Then you can call it from C# like this:
public async Task<ActionResult> MethodAsync(string connectionString, int? prm1, int? prm2)
{
List<MyModel> lst = new List<MyModel>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.OpenAsync();
using (var command = connection.CreateCommand())
{
command.CommandText = $"select * from MyFunc({prm1},{prm2})";
using (var reader = await command.ExecuteReaderAsync())
{
if (reader.HasRows)
{
while (await reader.ReadAsync())
{
MyModel myModel = new MyModel();
myModel.Column1 = int.Parse(reader["column1"].ToString());
myModel.Column2 = int.Parse(reader["column2"].ToString());
lst.Add(myModel);
}
}
}
}
}
View(lst);
}

How to pass a new dictionary to a method with a dictionary parameter?

my call -- I want to pass it a dictionary full of keys and values
QueryBuilder query = new QueryBuilder(new SqliteConnection(#"Data Source = D:/chinook.db"));
Dictionary<string, string> loadDictionary = new Dictionary<string, string>();
Console.WriteLine(query.Create("albums", loadDictionary)); //What do I replace loadDictionary with?
method name
public void Create(string tableName, Dictionary<string, string> columnsAndVals)
{
var commandText = $"insert into {tableName}(";
foreach (var item in columnsAndVals.Keys) // adds keys into command text
{
commandText += item + ",";
}
commandText += ") values(";
foreach (var item in columnsAndVals) // adds values into command text
{
commandText += $"#{item},";
}
var cmd = new SqliteCommand(commandText, Connection);
using (cmd)
{
foreach (var item in columnsAndVals)
{
cmd.Parameters.Add($"#{columnsAndVals.Keys}, #{columnsAndVals}");
}
cmd.ExecuteNonQuery();
}
}
I am constructing a method that will use a sql connection to insert a column and a value into a sql database.
To do this, I am using a dictionary to hold the columns and values I want to insert. (e.g. insert into (tableName)(columnsAndVals.Keys) values (columnsAndVals.Values) etc etc)
What I don't understand is how to establish a dictionary in my driver, then pass it as a parameter into my Create method. Is there a way to do this?
To create an insert query, in addition to the table name, you need to have three lists of values: column name, parameter name, and parameter value. While Dictionary<TKey, TValue allows you to pass only two lists (keys and values).
Therefore, you need to change the method signature. I would completely reject the Dictionary.
You can either pass three separate lists:
public int Create(string tableName, List<string> columnNames, List<string> parameterNames, List<object> parameterValues)
either a single list with a specially created class:
class QueryValue
{
public string ColumnName { get; set; }
public string ParameterName { get; set; }
public object ParameterValue { get; set; }
}
public int Create(string tableName, List<QueryValue> values)
{
var columnNames = string.Join(",", values.Select(v => v.ColumnName));
var parameterNames = string.Join(",", values.Select(v => v.ParameterName));
var sb = new StringBuilder()
.Append("insert into ")
.Append(tableName)
.Append('(')
.Append(columnNames)
.Append(") values(")
.Append(parameterNames)
.Append(')');
var commandText = sb.ToString();
//Console.WriteLine(commandText); // to see result
using (var cmd = new SqliteCommand(commandText, Connection))
{
foreach (var value in values)
{
cmd.Parameters.Add(value.ParameterName, value.ParameterValue);
}
return cmd.ExecuteNonQuery();
}
}
Call the method like this:
var values = new List<QueryValue>
{
new QueryValue{ ColumnName="a", ParameterName="#a", ParameterValue="aa" },
new QueryValue{ ColumnName="b", ParameterName="#b", ParameterValue="bb" },
new QueryValue{ ColumnName="c", ParameterName="#c", ParameterValue="cc" }
};
int rowsAffected = Create("albums", values);
Note that the ExecuteNonQuery method returns the number of rows affected. Therefore, our Create method returns int.
You can also use a tuple instead of a separate class.
However, I strongly oppose such methods. Their use looks rather crooked and fraught with errors.
Each table must have its own insertion method defined, as well as selections, updates, and deletions.
For example
public int InsertUser(int id, string name)
{
var commandText = "insert into User (id, name) values (#id, #name)";
using (var cmd = new SqlCommand(commandText, Connection))
{
cmd.Parameters.Add("id", id);
cmd.Parameters.Add("name", name);
return cmd.ExecuteNonQuery();
}
}
Of course, there will be many such methods, but their use is much easier and less error prone.

Passing function with parameter, as a parameter and returning a generic type

In the code below I am trying to pass a function func in to the GetData function. This would take the reader object and map it to a generic object.
I was hoping to pass GetData an object type along with a function to map data to that object type so I didn't have to repeatedly open / close / dispose the connection.
Is this possible or does anyone have any alternative suggestions?
public T GetData<T>(string cmdText,Func<T> func)
{
using (SqlConnection conn = new SqlConnection(connectionStringBuilder.ConnectionString))
{
using (SqlCommand cmd = new SqlCommand(cmdText, conn))
{
SqlDataReader reader = cmd.ExecuteReader();
//return func(reader);
// WITHIN THE FUNC FUNCTION:
// while (reader.Read())
// {
// Map function to T e.g
// T.property = reader["column"];
// Return T
// }
}
}
}
The signature that you're looking for is this:
T GetData<T>(string cmdText, Func<SqlDataReader, T> func)
Then you can go ahead and write your function as this:
public T GetData<T>(string cmdText, Func<SqlDataReader, T> func)
{
using (var conn = new SqlConnection(connectionStringBuilder.ConnectionString))
{
using (var cmd = new SqlCommand(cmdText, conn))
{
var reader = cmd.ExecuteReader();
return func(reader);
}
}
}
And you would use it like this:
var result = GetData("select * from Foo", dr =>
{
while (dr.Read())
{
return new { property = dr["column"] };
}
throw new DataException();
});
Now that's based on how you said you'd like to use it in your question.
However, you've made the use of the function a bit hard on yourself as you've split the implementation - part is in GetData and part is in the calling code.
You're better off using this signature:
IEnumerable<T> GetData<T>(string cmdText, Func<SqlDataReader, T> func)
Now you can write the method like this:
public IEnumerable<T> GetData<T>(string cmdText, Func<SqlDataReader, T> func)
{
using (var conn = new SqlConnection(connectionStringBuilder.ConnectionString))
{
using (var cmd = new SqlCommand(cmdText, conn))
{
var reader = cmd.ExecuteReader();
while (reader.Read())
{
yield return func(reader);
}
}
}
}
The advantage now is that the calling code is much simpler:
var results = GetData("select * from Foo", dr => new { property = dr["column"] });
This returns as many rows of data as your query returns.
If you know that your calling code only returns a single value, then you can drop a .Single() at the end of the method call to ensure you get one and only one result.

How to create a Dynamic IEnumerable

I know this is a simple question for you. But I am a beginner in c#.
What I want to achieve is to create a method that will store any type of List of Objects from my Model. e.g List<Person>.
I have tried to make something like this..
public IEnumerable<T> GetObjects<T>()
{
Type type = typeof(T);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo item in properties)
{
// store properties
}
List<T> objects = new List<T>();
using (SqlConnection str = GetSqlConnection)
{
// retrieve data from db
//then store it to list of objects
}
return objects;
}
This will enable me to retrieve data using only this method.
EDIT:
I already manage to create this sample code to retrieve a specific table from a database.
public IEnumerable<ItemBrand> getAllBrand
{
get
{
List<ItemBrand> brands = new List<ItemBrand>();
using (MySqlConnection strConn = getMySqlConnection())
{
string query = "SELECT * FROM tblbrand";
MySqlCommand cmd = new MySqlCommand(query, strConn);
strConn.Open();
MySqlDataReader rd = cmd.ExecuteReader();
while (rd.Read())
{
ItemBrand brand = new ItemBrand();
brand.brandID = Convert.ToInt32(rd["brandID"]);
brand.Name = rd["brandName"].ToString();
brands.Add(brand);
}
return brands;
}
}
}
Currently I have multiple methods of this in my solution. I would love to remove those duplicate codes with your help.
<code>
// Create a new type of the entity I want
Type t = typeof(T);
T returnObject = new T();
for (int i = 0; i < dataReader.FieldCount; i++) {
string colName = string.Empty;
colName = dataReader.GetName(i);
// Look for the object's property with the columns name, ignore case
PropertyInfo pInfo = t.GetProperty(colName.ToLower(), BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
// did we find the property ?
if (pInfo != null) {
if (dataReader.Read()) {
object val = dataReader[colName];
// is this a Nullable<> type
bool IsNullable = (Nullable.GetUnderlyingType(pInfo.PropertyType) != null);
if (IsNullable) {
if (val is System.DBNull) {
val = null;
} else {
// Convert the db type into the T we have in our Nullable<T> type
val = Convert.ChangeType(val, Nullable.GetUnderlyingType(pInfo.PropertyType));
}
} else {
// Convert the db type into the type of the property in our entity
val = Convert.ChangeType(val, pInfo.PropertyType);
}
// Set the value of the property with the value from the db
pInfo.SetValue(returnObject, val, null);
}
}
}
</code>
Have a generic method like:
public IEnumerable<T> CreateListOfItems(string tableName,
Func<MySqlDataReader, T> itemCreator)
{
var items = new List<T>();
using (var strConn = getMySqlConnection())
{
string query = "SELECT * FROM " + tableName;
var cmd = new MySqlCommand(query, strConn);
strConn.Open();
var rd = cmd.ExecuteReader();
while (rd.Read())
{
items.Add(itemCreator(rd));
}
}
return items;
}
Then use like this:
private ItemBrand CreateItemBrandFromDBData(MySqlDataReader rd)
{
return new ItemBrand
{
BrandID = Convert.ToInt32(rd["brandID"]),
Name = rd["brandName"].ToString()
};
}
...
var brands = CreateListOfItems<ItemBrand>(tblbrand, CreateItemBrandFromDBData);
I have written a method that gets the result retrieved from a stored proc and transformes it into a list of objects.
The only thing you need to pay attention to is:
The class you are mapping to must have the same column names as the ones that are sent from the database.
public IEnumerable <T> ExecuteReaderToList<T>(string storedProcedure, IDictionary parameters = null,
string connectionString = null)
{
ICollection list = new List();
var properties = typeof(T).GetProperties();
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand();
cmd.CommandText = storedProcedure;
cmd.CommandType = CommandType.StoredProcedure;
if (parameters != null)
{
foreach (KeyValuePair<string, object> parameter in parameters)
{
cmd.Parameters.AddWithValue(parameter.Key, parameter.Value);
}
}
cmd.Connection = conn;
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var element = Activator.CreateInstance<T>();
foreach (var f in properties)
{
var o = reader[f.Name];
if (o.GetType() != typeof(DBNull))
{
f.SetValue(element, o, null);
}
o = null;
}
list.Add(element);
}
}
conn.Close();
}
return list;
}

Parameterized SQL in C# .Net

If I have code like so:
public T ExecuteQuery<T>(Func<IDataReader, T> getResult, string query, params IDataParameter[] parameters)
{
using (SqlConnection conn = new SqlConnection(this.DefaultConnectionString))
{
conn.Open();
// Declare the parameter in the query string
using (SqlCommand command = new SqlCommand(query, conn))
{
foreach (var parameter in parameters)
{
command.Parameters.Add(parameter);
}
command.Prepare();
using (SqlDataReader dr = command.ExecuteReader())
{
return getResult(dr);
}
}
}
}
public string GetMySpecId(string dataId)
{
return ExecuteQuery(
dr =>
{
if (dr.Read())
{
return dr[0].ToString();
}
return string.Empty;
},
#"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId",
new SqlParameter("dataId", dataId));
}
}
How do I ensure that the
new SqlParameter("dataId", dataId));
piece of code is passing in a text or maybe an integer? Also how does the #"select..." actually work as I'm familiar to:
select id from mytable where dataId = #dataID;
I'm not sure that's parameterized properly. Take a look at the below modified code that will ensure the parameters are added properly and simplify the construction of the call to ExecuteQuery (in my opinion of course). This is pretty straight forward. The select statement is parameterized properly because it's using the #varname syntax:
"select \"specId\" from \"MyTable\" where \"dataId\" = #dataId"
Further, the parameters are typed properly because of the AddWithValue method:
command.Parameters.AddWithValue(parameter.Key, parameter.Value);
Finally, using the dictionary to send in the parameters keeps it pretty simple to construct the parameters from any structure, whether that be parameter values, or even an object.
public T ExecuteQuery<T>(Func<IDataReader, T> getResult, string query, Dictionary<string, object> parameters)
{
using (SqlConnection conn = new SqlConnection(this.DefaultConnectionString))
{
conn.Open();
// Declare the parameter in the query string
using (SqlCommand command = new SqlCommand(query, conn))
{
foreach (var parameter in parameters)
{
command.Parameters.AddWithValue(parameter.Key, parameter.Value);
}
command.Prepare();
using (SqlDataReader dr = command.ExecuteReader())
{
return getResult(dr);
}
}
}
}
public string GetMySpecId(string dataId)
{
return ExecuteQuery(
dr =>
{
if (dr.Read())
{
return dr[0].ToString();
}
return string.Empty;
},
"select \"specId\" from \"MyTable\" where \"dataId\" = #dataId",
new Dictionary<string, object>() { { "#dataId", dataId } });
}
P.S. - the # before the string in your example is just an escape sequence used in C#.

Categories

Resources