// Add into DB
using (tblArtworkTemplatesTableAdapter tblAdapter = new tblArtworkTemplatesTableAdapter())
{
tblAdapter.Insert(DateTime.Now, "#specID");
"#specID" = int.Parse(lstChooseSpec.SelectedValue)
}
I know the code is wrong, just for illustration of my objective, how do I paramatise the input?
Generally it depends. If You are using any kind of ORM like LINQ to SQL or NHibernate, it will do it for You no questions asked. If YOu are doing it using Plain ADO objects (which I suppose is the case) then You will have to comeup with the Command (or SQLCommand or any other ICommand implementation) object and use SQLParameter class (or other parameter classes).
ICommand has the collection of parameters that You can arbitralily edit.
SqlCommand cmd = new SqlCommand(
"select * from STH where column = #SpecID", conn);
//it might be useful to specify a type as well
SqlParameter param = new SqlParameter();
param.ParameterName = "#SpecID";
//I woudl use the TryParse method though
param.Value = int.Parse(lstChooseSpec.SelectedValue);
cmd.Parameters.Add(param);
This line
"#specID" = int.Parse(lstChooseSpec.SelectedValue)
Is incorrect. You can't assign a value to a constant. You might mean something like
specId = int.Parse(lstChooseSpec.SelectedValue);
The rest of the code is confusing. Why are you parsing lstChooseSpec.SelectedValue to an integer, then trying to add it to the adapter as a DateTime? C# is strongly-typed: something is either an int or a DateTime, but cannot be both.
It might help if you could post the rest of the method.
Also, have a look at this overview on MSDN.
Related
I am having the hardest time trying to neutralize this oracle not all variables bound. I tried the usual suggestions I got from some Google searches, but nothing seemed to help.
Eventually, for testing purposes, I reduced my query and code to a simple
public override List<DiscountList> GetDiscountList(string name)
{
string cmdText = "select discount from users where name = :Name";
DbParameters prms = new DbParameters(Ado.AdoTemplate.DbProvider);
prms.AddWithValue("Name", name);
List<DiscountList> list = Ado.AdoTemplate.QueryWithRowMapperDelegate<DiscountList>(CommandType.Text, cmdText,
new RowMapperDelegate<DiscountList>((reader, rowNum) =>
{
DiscountList item = new DiscountList();
item.Discount = reader.GetString(0, string.Empty);
return item;
})).ToList();
return list;
}
But I still receive the Oracle error, I even hardcoded the second parameter in AddWithValue to make sure it is not passing a null issue, but still the same error.
EDIT: To include whole function
Not sure why you are using DbParameters instead of OracleParameter like
command.Parameters.Add(New OracleParameter("Name", name));
(OR)
command.Parameters.AddWithValue("Name", name);
Without a bigger code snippet, it's hard to say if what you have done should work or not. To cut to the chase, this should be a working example of what I believe you are trying to do.
Note that the AddWithValue is very convenient and will nearly always result in the proper datatype mapping. If you really want to be iron-clad explicit, you can use the overload that specifies the datatype. If you ever start passing weird datatypes like Blobs, this might become important.
OracleCommand cmd = new OracleCommand("select discount from users where name = :Name",
conn);
cmd.Parameters.Add("Name", OracleDbType.VarChar);
cmd.Parameters[0].Value = name;
OracleDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
object discount = reader.GetValue(0);
}
reader.Close();
Turns out it was a silly mistake by me and I forgot an important piece of code:
})).ToList();
should be
}),prms).ToList();
I think the query should be like this:
string cmdText = "select discount from users where name = #Name";
Is it possible to parse sql parameters from plain commandtext?
e.g.
//cmdtext = SELECT * FROM AdWorks.Countries WHERE id = #id
SqlCommand sqlc = new SqlCommand(cmdtext);
SqlParameterCollection parCol = sqlc.Parameters //should contain now 1 paramter called '#id'
If a SQL Server is available, the best option may be to simply ask the server what it thinks; the server has parsing and metadata functions built in, for example sp_describe_undeclared_parameters.
I ended up with this extention method (since I don't think there's a built in function):
public static class SqlParExtension
{
public static void ParseParameters(this SqlCommand cmd)
{
var rxPattern = #"(?<=\= |\=)#\w*";
foreach (System.Text.RegularExpressions.Match item in System.Text.RegularExpressions.Regex.Matches(cmd.CommandText, rxPattern))
{
var sqlp = new SqlParameter(item.Value, null);
cmd.Parameters.Add(sqlp);
}
}
}
usage:
//cmdtext = SELECT * FROM AdWorks.Countries WHERE id = #id
SqlCommand sqlc = new SqlCommand(cmdtext);
sqlc.ParseParameters();
sqlc.Parameters["#id"].Value = value;
I will have to make sure about this but I'm sure you must add the range of parameters to the command. Like I say I will have to come back with this but you can try doing something like:
// Create a collection of parameters with the values that the procedure is expecting in your SQL client.
SqlParameter[] parameters = { new SqlParameter("#id", qid),
new SqlParameter("#otherValue", value) };
// Add teh parameters to the command.
sqlc.Parameters.AddRange(parameters)
You would be very welcome to have a look at my VS2015 extension, QueryFirst, that generates wrapper classes from .sql files, harvesting parameter declarations directly from your sql. You need to declare your parameters in the --designTime section of your request, but then you find them again directly as inputs to the Execute(), GetOne() or ExecuteScalar() methods. These methods return POCOs with meaningul property names. There's intellisense everywhere, and you don't have to type a line of parameter code, or connection code, or command code, or reader code, among NUMEROUS OTHER ADVANTAGES :-).
OK, nobody seems to know how to solve the problem I'm having looping through a cursor/result set for storage into a List, so I'm going to break it down into pieces and try to slog through it that way. So, first of all:
I add SQL Parameters to an OracleCommand object this way (works fine):
cmd.Parameters.Add("ABCID", _ABCID);
cmd.Parameters["ABCID"].Direction = ParameterDirection.Input;
cmd.Parameters["ABCID"].DbType = DbType.String;
IOW, when I add the param, I pass the name of the parameterized portion of the SQL ("ABCID" above) and a value to give it (_ABCID is a variable that has been assigned, let's say, "42").
However, when adding a Cursor (output) param, it seems to want, not a value (such as an initialized cursor object), but simply the data type:
cmd.Parameters.Add("cur", Devart.Data.Oracle.OracleDbType.Cursor);
cmd.Parameters["cur"].Direction = ParameterDirection.Output;
(I tried both ways, and neither one works, so...?)
Verily/thus, my question is: Is this really the correct way of declaring a Cursor parameter to be outputted back for traversal/access?
I'm using the brand new version of DevArt DotConnect components (6.80.332), VS 2010, .NET 4
Updated:
Here's the code in more context:
public void PopulateCurrentUserRoles(String AUserName, List<String> ACurrentUserRoles) {
_UserName = AUserName;
String query = "select roleid from ABCrole where ABCid = :ABCID";
Devart.Data.Oracle.OracleCommand cmd = new Devart.Data.Oracle.OracleCommand(query, con);
cmd.CommandType = CommandType.Text;
int _ABCID = GetABCIDForUserName();
cmd.Parameters.Add("cur", Devart.Data.Oracle.OracleDbType.Cursor);
cmd.Parameters["cur"].Direction = ParameterDirection.Output;
cmd.Parameters.Add("ABCID", _ABCID);
cmd.Parameters["ABCID"].Direction = ParameterDirection.Input;
cmd.Parameters["ABCID"].DbType = DbType.String;
//cmd.ExecuteNonQuery(); blows up: "illegal variable name/number"
//cmd.ExecuteCursor(); " "
//cmd.ExecuteReader(); " "
Devart.Data.Oracle.OracleCursor oraCursor =
(Devart.Data.Oracle.OracleCursor)cmd.Parameters["cur"].Value;
Devart.Data.Oracle.OracleDataReader odr = oraCursor.GetDataReader(); // "Object reference not set to an instance of an object"
while (odr.Read()) {
ACurrentUserRoles.Add(odr.GetString(0));
}
}
the following is from the Oracle Data Provider for .NET Developer's Guide. yes, I know, "Devart". Nonetheless, It suggests the following:
Be careful with your parameter typing declaration.
add that cursor/output parameter to the Parameters collection before
any others.
As a long shot... my guide shows a OracleDbType.RefCursor but not a OracleDbType.Cursor. If DevArt has RefCursor, try that. In visual studio, what type does .NET think that parameter is? This question is not as dumb as I used to think.
... On the other hand, if the parameter is set as an OracleDbType.Char type by setting the OracleDbType property, the output data is returned
as an OracleString type. If both DbType and OracleDbType properties
are set before the command execution, the last setting takes affect.
. . .
"An application should not bind a value for output parameters; it is
the responsibility of ODP.NET to create the value object and populate
the OracleParameter Value property with the object. When binding by
position (default) to a function, ODP.NET expects the return value to
be bound first, before any other parameters."
EDIT:
Based on #Clay's self-answer... so there is no parameter specified for the output, rather one simply does this: OracleDataReader odr = cmd.ExecuteReader();
Straight from the horse's mouth (the DevArt folks):
_UserName = AUserName;
// From the DevArtisans:
String query = "select roleid from ABCrole where ABCid = :ABCID";
Devart.Data.Oracle.OracleCommand cmd = new Devart.Data.Oracle.OracleCommand(query, con);
cmd.CommandType = CommandType.Text;
int _ABCID = GetABCIDForUserName();
cmd.Parameters.Add("ABCID", _ABCID);
cmd.Parameters["ABCID"].Direction = ParameterDirection.Input;
cmd.Parameters["ABCID"].DbType = DbType.String;
Devart.Data.Oracle.OracleDataReader odr = cmd.ExecuteReader();
while (odr.Read()) {
ACurrentUserRoles.Add(odr.GetString(0));
}
To quote Casey and the Sonshine Banned, "That's the way, Uh huh Uh huh, I like it, Uh huh Uh huh"; actually, I can't stand that crap, but I do kind of relate to that sentiment right about now.
Cannot figure out why I keep getting
An SqlParameter with ParameterName is not contained by this
SqlParameterCollection
when I've been pulling and calling procs like this fine in other methods.
The proc does have the one param #EqId in that casing.
List<string> equipTypes = new List<string>();
Database db = DatabaseFactory.CreateDatabase("OurDBName");
DbCommand cmd = db.GetStoredProcCommand("Get_EquipTypes_By_ID");
db.DiscoverParameters(cmd);
cmd.Parameters["#EqId"].Value = equipID;
using (IDataReader objReader = db.ExecuteReader(cmd))
{
while (objReader.Read())
equipTypes.Add(DataUtility.GetStringFromReader(objReader, "Data"));
}
Do your add parameters like this
cmd.Parameters.AddWithValue("#EqId", equipID);
I thinks it's the cleanest way to do it, well kind of :)
First off, I would not discover parameters when you are aware of the parameters. Since you are doing this, examine the parameter collection and make sure the parameter is being discovered. Fater you have that figured out, I would aim for something more like what Greg has suggested.
Since the OleDbParameter does not use named parameters (due to its nature),
why is it that the .NET OleDbParameter class expects a name? (string parametername ...)
All constructors require a parameter name, and I'm never sure what name to give it; my name is ok? or my grandmothers name?
Although the OleDb/Odbc providers use positional parameters instead of named parameters -
the parameters will need to be identified in some way inside the OleDbParameter collection should you need to reference them.
importantly when the parameterised sql statement is constructed, a variable for each parameter is declared and assigned it's respective value. and then used in the sql statement that is executed.
Take for example:
string sql = "SELECT * FROM MyTable WHERE Field = ?";
OleDbCommand cmd = new OleDbCommmand(sql, sqlConnection);
cmd.Parameters.Add("#FieldValue", OleDbType.Int32, 42);
OleDbDataReader dr = cmd.ExecuteReader();
The SQL executed would be something like (or close to I think):
DECLARE #FieldValue INT;
SET #FieldValue = 42;
SELECT * FROM MyTable WHERE Field = #FieldValue
You would be advised to use a parameter name that best matches the name of the column being operated on.
Just like everything else in programming, name it something meaningful to your context! (name, orderid, city, etc).
You use the names to access the parameters collection by name while in your c# code:
OleDbCommand command = new OleDbCommand();
...//Add your parameters with names, then reference them later if you need to:
command.Parameters["name"].Value = "your name or your grandmothers name here";
This is also useful when you use OUT parameters to extract the value after you execute your statement:
OleDbParameter outParam = new OleDbParameter();
outParam.Direction = ParameterDirection.Output;
outParam.DbType = DbType.Date;
outParam.ParameterName = "outParam";
command.Parameters.Add(outParam);
command.ExecuteNonQuery();
DateTime outParam = Convert.ToDateTime(command.Parameters["outParam"].Value);
There are interfaces and factories to provide a degree of independence of the underlying provider for data access - IDataParameter, DbParameter, DbProviderFactory etc.
To support this, all parameters can be named, even when the name is not used by the underlying provider.