I had to come back to an exiting project (hardly two months old) for a small change. I'm getting exceptions left and right on it now but it was in working condition and deployed to production and is running. The errors are pretty strange as well: It is all Oracle datatypes being read differently now. A Number(10) di column for a row was easily read as a DataRow and saved to a POCO's respective int property. But the same code is failing now.
Original code that was working before:
public static async Task<(OracleConnection connection, OracleDataAdapter adapter)> GetOraclePackage()
{
try
{
// To satisfy async. serves no purpose.
await Task.FromResult(0);
if (WebConfigurationManager.ConnectionStrings[Constants.ConnectionStringBilling] == null)
{
throw new ApplicationException("Oracle connection string is not found in web.config");
}
var oraConnection = new OracleConnection { ConnectionString = WebConfigurationManager.ConnectionStrings[Constants.ConnectionStringBilling].ConnectionString };
var oraDataAdapter = new OracleDataAdapter { SelectCommand = new OracleCommand { CommandType = CommandType.Text, Connection = oraConnection } };
return (oraConnection, oraDataAdapter);
}
catch (OracleException oraExp)
{
throw new RepositoryLayerException("Oracle error in repository's helper method GetOraclePackage() " + oraExp.Message, oraExp);
}
catch (Exception e)
{
throw new RepositoryLayerException("Error in repository's helper method GetOraclePackage() " + e.Message, e);
}
}
public async Task<IList<BDO.AirportServiceWithTerminal>> GetAllServices()
{
var objResult = new List<BDO.AirportServiceWithTerminal>();
var oraclePackage = await Helper.GetOraclePackage();
var ds = new DataSet();
try
{
var sql = #"
SELECT
s.service_code,
s.account_code,
s.service_fees,
s.customer_type,
s.period,
s.ser_name_e,
s.isvat,
s.vatvalue
FROM
serviceinfo s
WHERE
sevice_status = '1'
AND
service_fees > 1
AND
account_code IN (
'AB','BC','CD','DE'
)
";
oraclePackage.connection.Open();
oraclePackage.adapter.SelectCommand.CommandText = sql;
oraclePackage.adapter.Fill(ds, "ServiceList");
if (ds.Tables[0] != null)
{
foreach (DataRow oneService in ds.Tables[0].Rows)
{
var newTerminalService = new BDO.AirportServiceWithTerminal
{
Code = oneService.Field<int>("service_code"),
NameEnglish = oneService.Field<string>("ser_name_e").Replace(#"""", "").Trim(),
Fee = oneService.Field<decimal>("service_fees"),
IsVat = oneService.Field<string>("isvat") == "1",
VatValue = int.Parse(oneService.Field<string>("vatvalue"))
};
objResult.Add(newTerminalService);
}
}
}
catch (Exception exp)
{
throw new RepositoryLayerException("An error occurred while retrieving all services from billing system", exp);
}
finally
{
oraclePackage.adapter.Dispose();
if (oraclePackage.connection.State == ConnectionState.Open) oraclePackage.connection.Close();
oraclePackage.connection.Dispose();
}
return objResult;
}
And now, when it tries to cast the service_code to int, it fails. I tried to get the type of service_code as follows:
oneService["service_code"].GetType()
and I either get UnderlyingSystemType: {Name = "Int64" FullName = "System.Int64"} or UnderlyingSystemType: {Name = "Decimal" FullName = "System.Decimal"}
(Whe writing this SO question, I started to get Decimal)
Is there a way around this by doing some cast as I know the database has not been changed and I cannot just accept the new types as there are many. Same case is for decimals now reported as doubles.
In general case Oracle's Number(10) doesn't fit int (Int32) since Number(10) has a wider range:
Type Min Value Max Value
------------------------------------
Number(10) -9999999999 .. 9999999999
Int32 -2147483648 .. 2147483647
That's why, say, 9876543210 can be represented as Number(10) but not as int (and this is the very reason for .Net to map Number(10) to Int64). If values in the database are guaranteed to be small enough (and thus in fact fit int), try using Convert:
Code = Convert.ToInt32(oneService.Field<object>("service_code"));
...
VatValue = Convert.ToInt32(oneService.Field<object>("vatvalue"));
Related
I have a bit of code (using dapper) that is executing a query against my database (ASE 12). The sql has a where clause like this where r.reg_id = #reg_id
Now in the database the reg_id column is a type of numeric, length 6, Prec 18, Scale 0
In the code the #reg_id variable is a Int32 and I would expect an exception to occur when I call the async method query but it just never errors out...
QUESTION: Is there a setting/configuration I need to set in dapper to get datatype errors to kick out exceptions?
Code below will work because I set the datatype to decimal, but for other team members I would like for an exception to get kicked out so they know what the issue is... Any ideas?
public async Task<IEnumerable<CentersForRegIdResponse>> Handle(CentersForRegIdQuery request, CancellationToken cancellationToken)
{
StringBuilder sql = new StringBuilder();
sql.Append(" SELECT c.ctr_id, RTRIM(LTRIM(c.ctr_shname)) AS ctr_shname, RTRIM(LTRIM(r.reg_name)) AS reg_name ");
sql.Append(" FROM center c, ");
sql.Append(" region r ");
sql.Append(" WHERE c.jcc_active = 'Y' and c.reg_id = r.reg_id and r.reg_id = #reg_id ");
sql.Append(" ORDER BY c.ctr_shname ");
string query = sql.ToString();
// custom mapping
_dapperTools.DapperCustomMapping<CentersForRegIdResponse>();
try
{
using (IDbConnection dbConnection = _dapperTools._aseconnection)
{
// example get list of rows by args
var arguments = new
{
#reg_id = request.RegId
};
IEnumerable<CentersForRegIdResponse> allRows =
await dbConnection.QueryAsync<CentersForRegIdResponse>(query, arguments);
return allRows;
}
}
catch (Exception ex)
{
_dapperTools.ReportSqlError(_log, query, ex);
return null;
}
}
public class CentersForRegIdQuery : IRequest<IEnumerable<CentersForRegIdResponse>>
{
public Decimal RegId { get; set; }
}
I'm making a library for connecting an Informix database with a C# extension for use in Outsystems.
So, now I've hit a wall. I need to receive a list/multiset from the db. How can I do that? I'm using the IfxDataReader to receive the data. But I see no method that could work.
Here is how we can send a list/multiset input parameter. But I need to receive it from a result set.
EDIT: Seeing as this was frowned upon by someone I'll provide some code and my try at it to see if you think it's correct (can't test it right now as I don't yet have data on the database... I'll test it in the end):
ssBensAndFotos = new RLBensAndFotos_BemRecordList();
DatabaseConnection dc = new DatabaseConnection(ssDatabase, ssHost, ssServer, ssService, ssProtocol, ssUserID, ssPassword);
try
{
dc.Conn = new IfxConnection(dc.ConnectionString);
dc.Cmd = new IfxCommand("get_bens_and_fotos_by_id_diligencia", dc.Conn);
dc.Cmd.CommandType = CommandType.StoredProcedure;
dc.Cmd.Parameters.Add(new IfxParameter("pi_id_diligencia", IfxType.Integer) { Direction = ParameterDirection.Input, Value = sspi_id_diligencia });
dc.Conn.Open();
using (IfxDataReader reader = dc.Cmd.ExecuteReader())
if (reader.HasRows)
while (reader.Read())
{
var bensAndFotos = new STBensAndFotos_BemStructure()
{
ssid_bem = reader.GetInt32(0),
ssnumero = reader.GetInt32(1),
ssespecie = reader.GetString(2),
ssbem = reader.GetString(3),
ssvalor = reader.GetDecimal(4),
sscomum = reader.GetInt32(5),
ssvoice_record = AuxiliaryMethods.CreateByteArrayFromIfxBlob(reader.GetIfxBlob(6))
};
// Here I get the string, split it and check to see which members of the array are integers, since in this case I'll be getting a multiset of int's
var multisetString = reader.GetString(7).Split('\'');
int n;
foreach (var item in multisetString)
if (int.TryParse(item, out n))
bensAndFotos.ssfotos.Add(new STBensAndFotos_FotoStructure() { ssfoto = n });
ssBensAndFotos.Add(bensAndFotos);
}
dc.Conn.Close();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
LIST, MULTISET and SET are mapped to String. You should be able to use IfxDataReader.GetString() to get the data.
I am using MySql 5.6x with Visual Studio 2015, windows 10, 64-bit. C# as programming language. In my CRUD.cs (Class file) i have created the following method:
public bool dbQuery(string sql,string[] paramList= null)
{
bool flag = false;
try
{
connect();
cmd = new MySqlCommand(sql,con);
cmd.Prepare();
if(paramList != null){
foreach(string i in paramList){
string[] valus = i.Split(',');
string p = valus[0];
string v = valus[1];
cmd.Parameters[p].Value = v;
}
}
if (cmd.ExecuteNonQuery() > 0)
{
flag = true;
}
}
catch (Exception exc)
{
error(exc);
}
}
I am passing the query and Parameters List like this:
protected void loginBtn_Click(object sender, EventArgs e)
{
string sql = "SELECT * FROM dept_login WHERE (user_email = ?user_email OR user_cell = ?user_cell) AND userkey = ?userkey";
string[] param = new string[] {
"?user_email,"+ userid.Text.ToString(),
"?user_cell,"+ userid.Text.ToString(),
"?userkey,"+ userkey.Text.ToString()
};
if (db.dbQuery(sql, param))
{
msg.Text = "Ok";
}
else
{
msg.Text = "<strong class='text-danger'>Authentication Failed</strong>";
}
}
Now the problem is that after the loop iteration complete, it directly jumps to the catch() Block and generate an Exception that:
Parameter '?user_email' not found in the collection.
Am i doing this correct to send params like that? is there any other way to do the same?
Thanks
EDIT: I think the best way might be the two-dimensional array to collect the parameters and their values and loop then within the method to fetch the parameters in cmd.AddWidthValues()? I may be wrong...
In your dbQuery you don't create the parameters collection with the expected names, so you get the error when you try to set a value for a parameter that doesn't exist
public bool dbQuery(string sql,string[] paramList= null)
{
bool flag = false;
try
{
connect();
cmd = new MySqlCommand(sql,con);
cmd.Prepare();
if(paramList != null){
foreach(string i in paramList){
string[] valus = i.Split(',');
string p = valus[0];
string v = valus[1];
cmd.Parameters.AddWithValue(p, v);
}
}
if (cmd.ExecuteNonQuery() > 0)
flag = true;
}
catch (Exception exc)
{
error(exc);
}
}
Of course this will add every parameter with a datatype equals to a string and thus is very prone to errors if your datatable columns are not of string type
A better approach would be this one
List<MySqlParameter> parameters = new List<MySqlParameter>()
{
{new MySqlParameter()
{
ParameterName = "?user_mail",
MySqlDbType= MySqlDbType.VarChar,
Value = userid.Text
},
{new MySqlParameter()
{
ParameterName = "?user_cell",
MySqlDbType= MySqlDbType.VarChar,
Value = userid.Text
},
{new MySqlParameter()
{
ParameterName = "?userkey",
MySqlDbType = MySqlDbType.VarChar,
Value = userkey.Text
},
}
if (db.dbQuery(sql, parameters))
....
and in dbQuery receive the list adding it to the parameters collection
public bool dbQuery(string sql, List<MySqlParameter> paramList= null)
{
bool flag = false;
try
{
connect();
cmd = new MySqlCommand(sql,con);
cmd.Prepare();
if(paramList != null)
cmd.Parameters.AddRange(paramList.ToArray());
if (cmd.ExecuteNonQuery() > 0)
{
flag = true;
}
}
catch (Exception exc)
{
error(exc);
}
}
By the way, unrelated to your actual problem, but your code doesn't seem to close and dispose the connection. This will lead to very nasty problems to diagnose and fix. Try to use the using statement and avoid a global connection variable
EDIT
As you have noticed the ExecuteNonQuery doesn't work with a SELECT statement, you need to use ExecuteReader and check if you get some return value
using(MySqlDataReader reader = cmd.ExecuteReader())
{
flag = reader.HasRows;
}
This, of course, means that you will get troubles when you want to insert, update or delete record where instead you need the ExecuteNonQuery. Creating a general purpose function to handle different kind of query is very difficult and doesn't worth the work and debug required. Better use some kind of well know ORM software like EntityFramework or Dapper.
Your SQL Commands' Parameters collection does not contain those parameters, so you cannot index them in this manner:
cmd.Parameters[p].Value = v;
You need to add them to the Commands' Parameters collection in this manner: cmd.Parameters.AddWithValue(p, v);.
I am retrieving a moderate amount of data and processing it - nothing unique there. What was odd at first was that with some sets of data, it worked fine, and with others, I got the following err msg:
This err msg seems to be total hogwash, though (misleading, at any rate), because there is no more data with the failing set than with the successful set, and so it shouldn't take any longer to run the "bad" data than the good.
Mor enlightening, perhaps, are the other err msgs that appear after that:
Note: I get these verbose err dialogs, rather than something more concise, because of the debugging code you can see in the catch block of the code below.
So it seems apparent that it's not really a "too much data" issue, as the initial err msg indicates. It's also not a "missing data" issue, because if I query for data from December 2014 through December 2015 (for which upcoming month there is no data), it runs fine -- it just returns all 0s for December 2015; so it must be a "bad (not just missing) data" issue. How can I determine what the bad data is and defensively prevent it from ruining the run of the app?
Line 601, implicated in the err msg above, contains this code:
private void ReadData(string _unit, string monthBegin, string monthEnd, string beginYear, string endYear)
{
try
{
String dateBegin = UsageRptConstsAndUtils.GetYYYYMMDD(monthBegin, beginYear, true);
String dateEnd = UsageRptConstsAndUtils.GetYYYYMMDD(monthEnd, endYear, false);
DateTime dtBegin = UsageRptConstsAndUtils.DatifyYYYYMMDD(dateBegin);
DateTime dtEnd = UsageRptConstsAndUtils.DatifyYYYYMMDD(dateEnd);
DataTable dtUsage = SqlDBHelper.ExecuteDataSet("sp_ViewProductUsage_MappingRS", CommandType.StoredProcedure,
new SqlParameter() { ParameterName = "#Unit", SqlDbType = SqlDbType.VarChar, Value = _unit },
new SqlParameter() { ParameterName = "#BegDate", SqlDbType = SqlDbType.DateTime, Value = dtBegin },
new SqlParameter() { ParameterName = "#EndDate", SqlDbType = SqlDbType.DateTime, Value = dtEnd }
);
SqlDBHelper.ExecuteDataSet() is:
public static DataTable ExecuteDataSet(string sql, CommandType cmdType, params SqlParameter[] parameters)
{
using (DataSet ds = new DataSet())
using (SqlConnection connStr = new SqlConnection(UsageRptConstsAndUtils.CPSConnStr))
using (SqlCommand cmd = new SqlCommand(sql, connStr))
{
cmd.CommandType = cmdType;
foreach (var item in parameters)
{
cmd.Parameters.Add(item);
}
try
{
cmd.Connection.Open();
new SqlDataAdapter(cmd).Fill(ds);
}
catch (SqlException sqlex)
{
for (int i = 0; i < sqlex.Errors.Count; i++)
{
var sqlexDetail = String.Format("From ExecuteDataSet(), SQL Exception #{0}{1}Source: {2}{1}Number: {3}{1}State: {4}{1}Class: {5}{1}Server: {6}{1}Message: {7}{1}Procedure: {8}{1}LineNumber: {9}",
i + 1, // Users would get the fantods if they saw #0
Environment.NewLine,
sqlex.Errors[i].Source,
sqlex.Errors[i].Number,
sqlex.Errors[i].State,
sqlex.Errors[i].Class,
sqlex.Errors[i].Server,
sqlex.Errors[i].Message,
sqlex.Errors[i].Procedure,
sqlex.Errors[i].LineNumber);
MessageBox.Show(sqlexDetail);
}
}
catch (Exception ex)
{
String exDetail = String.Format(UsageRptConstsAndUtils.ExceptionFormatString, ex.Message, Environment.NewLine, ex.Source, ex.StackTrace);
MessageBox.Show(exDetail);
}
return ds.Tables[0];
}
}
Line 396 (referenced in the last err msg) is the first line of code here:
private String GetContractedItemsTotal()
{
var allContractRecords = _itemsForMonthYearList.Where(x => x.ContractItem);
var totalContractItemPurchases = allContractRecords.Sum(x => x.TotalPurchases);
return totalContractItemPurchases.ToString("C");
}
What could be causing this code to sometimes crash with the "Cannot find Table 0" and "Value cannot be null" exceptions? Or more to the point, how can I prevent it from wreaking such havoc when a value is null?
Some more context:
_itemsForMonthYearList is defined like this:
private List<ItemsForMonthYear> _itemsForMonthYearList;
..and populated like so:
var ifmy = new ItemsForMonthYear();
int qty = Convert.ToInt32(productUsageByMonthDataRow["TotalQty"]);
// TotalPrice as Decimal for calculation
Decimal totPrice = Convert.ToDecimal(productUsageByMonthDataRow["TotalPrice"]);
Decimal avgPrice = Convert.ToDecimal(productUsageByMonthDataRow["AvgPrice"]);
String monthYear = productUsageByMonthDataRow["MonthYr"].ToString();
ifmy.ItemDescription = desc;
ifmy.TotalPackages = qty;
ifmy.TotalPurchases = totPrice;
ifmy.AveragePrice = avgPrice;
ifmy.monthYr = monthYear;
ifmy.ContractItem = contractItem; // added 11/16/2016
if (null == _itemsForMonthYearList)
{
_itemsForMonthYearList = new List<ItemsForMonthYear>();
}
_itemsForMonthYearList.Add(ifmy);
As jmcilhinney suggests, tweaking the CommandTimeout value seems to have been the ticket/done the trick.
I originally made the SqlCommand's CommandTimeout value 300 (5 minutes), but with that I got "Context Switch Deadlock occurred." So I then reduced it to 120 (2 minutes), and that seems to be more or less the "sweet spot" for me. I did get "Timeout expired" one time out of several tests, but when I retried the same exact range, it completed successfully the second time, so I guess it's just "one of those things" - 120 will sometimes not be enough of a timeout, but 300 is apparently too much. IOW, this balancing act between too little and too much doesn't appear to be "an exact science."
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
This SqlCe code looks awfully strange to me:
cmd.CommandText = "INSERT INTO departments ( account_id, name) VALUES (?, ?)";
foreach(DataTable tab in dset.Tables)
{
if (tab.TableName == "Departments")
{
foreach(DataRow row in tab.Rows)
{
Department Dept = new Department();
if (!ret)
ret = true;
foreach(DataColumn column in tab.Columns)
{
if (column.ColumnName == "AccountID")
{
Dept.AccountID = (string) row[column];
}
else if (column.ColumnName == "Name")
{
if (!row.IsNull(column))
Dept.AccountName = (string) row[column];
else
Dept.AccountName = "";
}
}
List.List.Add(Dept);
. . .
dSQL = "INSERT INTO departments ( account_id, name) VALUES ('" + Dept.AccountID + "','" + Dept.AccountName +"')";
if (!First)
{
cmd.Parameters[0].Value = Dept.AccountID;
cmd.Parameters[1].Value = Dept.AccountName;
}
if (First)
{
cmd.Parameters.Add("#account_id",Dept.AccountID);
cmd.Parameters.Add("name",Dept.AccountName);
cmd.Prepare();
First = false;
}
if (frmCentral.CancelFetchInvDataInProgress)
{
ret = false;
return ret;
}
try
{
dbconn.DBCommand( cmd, dSQL, true );
}
. . .
public void DBCommand(SqlCeCommand cmd, string dynSQL, bool Silent)
{
SqlCeTransaction trans = GetConnection().BeginTransaction();
cmd.Transaction = trans;
try
{
cmd.ExecuteNonQuery();
trans.Commit();
}
catch (Exception ex)
{
try
{
trans.Rollback();
}
catch (SqlCeException)
{
// Handle possible exception here
}
MessageBox.Show("DBCommand Except 2"); // This one I haven't seen...
WriteDBCommandException(dynSQL, ex, Silent);
}
}
My questions are:
1) Should "?" really be used in the assignment to cmd.CommandText, or should "#" be used instead?
2) One of the "cmd.Parameters.Add()"s (account_id) uses a "#" and the other (name) doesn't. Which way is right, or is the "#" optional?
3) I can't make heads or tails of why DBCommand() is written as it is - the final two args are only used if there's an exception...???
I'm tempted to radically refactor this code, because it seems so bizarre, but since I don't really understand it, that might be a recipe for disaster...
I'm fairly certain this article will answer some of your questions:
http://msdn.microsoft.com/en-us/library/yy6y35y8.aspx
The second chart explains the difference between the named and positional (?) parameters (used in OleDb and ODBC).
I believe in the case where the ? is used, the # is optional, but I'm not sure of this. If it's working, I'd say that that IS the case.
The stuff in DBCommand appears to simply be there for logging purposes. If the exection fails, it tries to do a rollback and then logs the exception with the sql command (in dynSQL).
The ? parameter is older Access syntax.
My guess is this used to be an Access database, but someone converted it to SQL CE at some point.
Generally, SQL understands that ? parameter, but it's better to just change that while you are in there so that it is more understood.
I'm still trying to make heads & tails of all these variables. If I get it sorted out, I'll post up compilable (sp?) code.
EDIT: I had to put this into a method and work out all of the RED errors to make sure I wasn't giving you something that would not compile.
I passed it your DataSet like so, with lots of comments added:
private bool StrangeSqlCeCode(DataSet dset) {
const string ACCOUNT_ID = "AccountID";
const string DEPARTMENTS = "Departments";
const string NAME = "Name";
const string SQL_TEXT = "INSERT INTO departments (account_id, name) VALUES (#account_id, #name)";
bool ret = false;
//bool First = false; (we don't need this anymore, because we initialize the SqlCeCommand correctly up front)
using (SqlCeCommand cmd = new SqlCeCommand(SQL_TEXT)) {
// Be sure to set this to the data type of the database and size field
cmd.Parameters.Add("#account_id", SqlDbType.NVarChar, 100);
cmd.Parameters.Add("#name", SqlDbType.NVarChar, 100);
if (-1 < dset.Tables.IndexOf(DEPARTMENTS)) {
DataTable tab = dset.Tables[DEPARTMENTS];
foreach (DataRow row in tab.Rows) {
// Check this much earlier. No need in doing all the rest if a Cancel has been requested
if (!frmCentral.CancelFetchInvDataInProgress) {
Department Dept = new Department();
if (!ret)
ret = true;
// Wow! Long way about getting the data below:
//foreach (DataColumn column in tab.Columns) {
// if (column.ColumnName == "AccountID") {
// Dept.AccountID = (string)row[column];
// } else if (column.ColumnName == "Name") {
// Dept.AccountName = !row.IsNull(column) ? row[column].ToString() : String.Empty;
// }
//}
if (-1 < tab.Columns.IndexOf(ACCOUNT_ID)) {
Dept.AccountID = row[ACCOUNT_ID].ToString();
}
if (-1 < tab.Columns.IndexOf(NAME)) {
Dept.AccountName = row[NAME].ToString();
}
List.List.Add(Dept);
// This statement below is logically the same as cmd.CommandText, so just don't use it
//string dSQL = "INSERT INTO departments ( account_id, name) VALUES ('" + Dept.AccountID + "','" + Dept.AccountName + "')";
cmd.Parameters["#account_id"].Value = Dept.AccountID;
cmd.Parameters["#name"].Value = Dept.AccountName;
cmd.Prepare(); // I really don't ever use this. Is it necessary? Perhaps.
// This whole routine below is already in a Try/Catch, so this one isn't necessary
//try {
dbconn.DBCommand(cmd, true);
//} catch {
//}
} else {
ret = false;
return ret;
}
}
}
}
return ret;
}
I wrote an overload for your DBCommand method to work with Legacy code:
public void DBCommand(SqlCeCommand cmd, string dynSQL, bool Silent) {
cmd.CommandText = dynSQL;
DBCommand(cmd, Silent);
}
public void DBCommand(SqlCeCommand cmd, bool Silent) {
string dynSQL = cmd.CommandText;
SqlCeTransaction trans = GetConnection().BeginTransaction();
cmd.Transaction = trans;
try {
cmd.ExecuteNonQuery();
trans.Commit();
} catch (Exception ex) {
try {
trans.Rollback(); // I was under the impression you never needed to call this.
// If Commit is never called, the transaction is automatically rolled back.
} catch (SqlCeException) {
// Handle possible exception here
}
MessageBox.Show("DBCommand Except 2"); // This one I haven't seen...
//WriteDBCommandException(dynSQL, ex, Silent);
}
}