Update iSeries DATE/TIME/TIMESTAMP columns from .Net - c#

I have a .Net application using EF4 and both DB2 LUW and iSeries.
When we run a command on the DB2, we clone the original command into a new iDB2Command, copy the parameters, and run. It all works great, until we get to DATE columns. At that point the AS400 or the driver seems to fall down: it gets a parameter that says it is a DbTypes.DateTime, and contains a DateTime object, but the column is DATE.
The error returned (on LUW) is below. The AS400 (v6r1m0) returns a slightly different wording
ERROR [22008] [IBM] CLI0114E Datetime field overflow. SQLSTATE=22008
The code looks something like this (and is totally iSeries/DB2-LUW agnostic)
// all generic System.Data classes, no iDB2Connection datatype. The driver isn't even
// installed on dev/build machines at this point. We rely on .Net reading the connection
// string from App.config to create the proper DB Driver (db2-luw or iSeries)
DbConnection c = ... get connection from somewhere...
DbCommand cmd = c.CreateCommand();
var p = cmd.CreateParameter();
p.ParamterName = "V_XXX_XXX";
p.DbType = DbTypes.DateTime;
p.Value = DateTime.Now;
cmd.AddParamter(p);
...
So...
Is there anything we are doing wrong here? For LUW sending the parameter as a DbTypes.DateTime works just fine. EDIT: It worked just fine on LUW because we were sending a truncated date in local test code (eg, Now.Date). Normal DateTime.Now fails with truncation error just like on the AS400)
Also, we have complete metadata on the type, so in theory it is possible to tell, at conversion time, what System.DbTypes to convert to. We are hoping that is all that needs to be done (or hacky convert-to-string stuff), rather than some underlying issue.
** Solution **
Thanks to #mike-willis, we just check column before creating the command and do a manual truncation when required.
// get the db2 column type from our model metadata, because in .net it is all just DateTime
cmd.AddParamter("#MyDateColumn", FixParam( dateObject, metatdata.ColumnType);
// fix up different types of parameters. real version does a lot more validation
public object FixParam(object value, string db2columnType) {
if (db2columnType == "date") return ((DateTime)value).Date;
...
return value;
}
Thanks, all you DB2 folks.

Coming from the i, you can just assign from a DATE field to a DateTime field.
DateTime EmployeeSignedDateTime = i.Field<DateTime>("EMP_SIGNED_DATE").Add(i.Field<DateTime>("EMP_SIGNED_TIME").TimeOfDay)
In order to sent to the i, you can do that following:
p.Add("#EMPLOYEE_SIGNED_DATE", iDB2DbType.iDB2Date).Value = DateTime.Now.Date;
p.Add("#EMPLOYEE_SIGNED_TIME", iDB2DbType.iDB2Time).Value = DateTime.Now.ToString("HH.mm.ss");
Note that I am using the IBM.Data.DB2.iSeries.dll.

Related

SQLite.Net Extensions Data insertion error on Windows Phone 8.1 Runtime project

I have been using the SQLiteAsyncConnection for all the database operations and it was recently I came to know that it doesn't allow association between tables. Now I am in the process of moving the code to use the SQLite.Net Extension which supports extensions. I have found a weird issue when I insert datetime data into the table. It completely changes the datetime on insertions.
App.db2.Insert(new FrequentlyAssignedShifts()
{
ShiftStart = Convert.ToDateTime(btnShiftStart.Content.ToString()),
ShiftEnd = Convert.ToDateTime(btnShiftEnd.Content.ToString()),
});
And this is how the connection is established.
string databasePath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Scheduler.sqlite");
public static SQLiteConnection db2;
var platform = new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT();
db2 = new SQLiteConnection(platform, databasePath);
I checked until the last point using breakpoints, whether the right data is getting inserted. It all seems fine, it is getting the right date from the button controls, but the moment it passes this part of the code the date value changes. The "ShiftStart & ShiftEnd" variables are datetime variables in the Table. Could someone please advise.
Thanks for reading the post.
Maybe too late but as far I can see, the time is saved as ticks on the database, so instead of using "Date" or "Timestamp" or anything else, use bigint, thus, the field will hold the time as ticks using UTC, once you retrieve the value from database use the function "ToLocalTime()" from DateTime and vioala!!!!

MySQL Connector/NET's MySqlCommand not using Parameters

So I'm having some issues with the MySQL connector (specifically for .NET). Normally, I'd just not use parameterization -- although I realize that doing so leaves my program vulnerable to SQL injection.
Unfortunately, however, I can't just do that; I need to store an array of bytes (those which are derived from reading an image file on the client side of this program) into a MySQL database. Obviously, putting the byte array into the query string isn't going to work; that'll just put "System.Byte[]" into the array.
I've used parameterized queries before -- but only in java. The following is my code, where packet is an object I've guaranteed will return correct data in all circumstances (for testing purposes, I've even added a driver class which has all hard-coded data) and the Server singleton's connection is guaranteed to be open and valid.
MySqlCommand cmd = new MySqlCommand("insert into `screenshots` (`playerId`, `serverId`, `data`, `uploadDate`, `uploadingUserId`) values(?playerId, ?serverId, \"?attachmentData\", \"?dateTime\", ?userId,)", Server.getSingleton().getDbManager().getConnection());
cmd.Parameters.AddWithValue("?playerId", packet.getPlayerId()); //string of an integer
cmd.Parameters.AddWithValue("?serverId", packet.getServerId()); //string of an integer
cmd.Parameters.AddWithValue("?attachmentData", packet.getAttachmentData()); //byte[]
cmd.Parameters.AddWithValue("?dateTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
cmd.Parameters.AddWithValue("?userId", packet.getUserId()); //string of an integer
cmd.executeNonQuery();
The issue I'm having is that the MySql Connector/NET seems to never replace the parameters with their values. I've tried instantiating a new MySqlParameter object, setting its Value, then calling cmd.Parameters.Add(...), but that presents the same issue.
I took a look at the MySQL documentation, and it says to use MySqlCommmand#Parameters.AddWithValue(string, object), and there are no other steps that I'm missing.
Whenever I attach Visual Studio's debugger to the process, it is visible that the parameters have all been added to the Parameters list property; however, when the query executes, the Just-in-Time Debugger halts execution and highlights the line with cmd.executeNonQuery() on it, saying that '?dateTime' is not a valid value for column 'dateTime' (where the column dateTime is of SQL type DateTime) or something to that extent.
This obviously means that the parameters are not being replaced with their values. ?dateTime isn't a valid value for a DateTime field because it has to be in format of yyyy-MM-dd HH:mm:ss, so an exception is thrown by the Connector. But what am I doing wrong?
Thanks in advance for any help -- I'm used to doing database-related things in Java (along with socket logic and the like), but this is a project for school which requires it be done in C#.
Those don't look like valid MySQL parameters; if you want named parameters, use #yournamehere, like so:
MySqlCommand cmd = new MySqlCommand("insert into `screenshots` (`playerId`, `serverId`, `data`, `uploadDate`, `uploadingUserId`) values(#playerId, #serverId, #attachmentData, #dateTime, #userId)", Server.getSingleton().getDbManager().getConnection());
cmd.Parameters.AddWithValue("#playerId", packet.getPlayerId());
You also shouldn't quote a parameter; ADO.NET will do that as necessary, based off the target column's datatype.
Source: http://dev.mysql.com/doc/connector-net/en/connector-net-tutorials-parameters.html

Updating EF model throws ""Arithmetic overflow error converting expression to data type datetime"

I an create a new record with no issues, but whenever I update the model, even when the date isn't being changed, I get the "Arithmetic overflow error converting expression to data type datetime" error.
I have confirmed that the dates in the existing db record to be updated are valid.
The below code snippet, will fail even though no date field is being updated.
var dbModule = db.Modules.FirstOrDefault(v => v.ModuleId == module.ModuleId);
dbModule.Name = module.Name;
//dbModule.Password = module.Password;
//dbModule.Colour = module.Colour;
//dbModule.Modified = module.Modified;
db.SaveChanges();
If I look at the trace, I see the command below, so am certain that no date fields are being updated.
ADO.NET:Execute NonQuery "update [dbo].[Modules]
set [Name] = #0
where ([ModuleId] = #1)"
For clarity, the operating environment is:
ASP.NET MVC 3
.NET 4.5
SQL Server 2012 (Express)
Models generated by database first approach, with corresponding edmx.
DateTime model fields are of type DateTime in SQL Server
I have tried to use SQL Server Profiler, but it does't output the update statement to see the exact SQL, so am relying on the intellitrace provided above.
Any help would be greatly appreciated.
Aughhhhh! After days and days.
Using SQL Server Profiler, I found that I had to log both
RPC:Completed
SQL:BatchCompleted
That showed all the SQL statements, which allowed me to track it down to the fact that I had a trigger firing on the database, setting the Modified field, and using an incorrectly formatted string date.
BlinkyBill, check if the datetime field on your entity/db is nullable, and if you are using the correct mapping.
Also, you could try to explicity set this field on your update method.
Hope it helps!

ODP.NET : Parameter Types "cached" on identical CommandText

I'm currently evaluating Oracle's ODP.NET DataProvider and I ran into a problem that popped up in one of our testcases: When the same command text is executed with different parameter types, the parameter type of the first executed command is used in all following commands.
Take for example the following code:
const int sampleInt32 = 1234567890;
const string sampleNvarchar = "someTestString";
const string sqlCommandtext = "SELECT :PARAM PARAM FROM DUAL";
using (OracleConnection connection = new OracleConnection(builder.ConnectionString))
{
connection.Open();
//Test 1 - Int 32
using (OracleCommand commandInt32 = connection.CreateCommand())
{
commandInt32.CommandText = sqlCommandtext;
commandInt32.Parameters.Add("PARAM", OracleDbType.Int32, sampleInt32, ParameterDirection.Input);
using (IDataReader reader = commandInt32.ExecuteReader())
{
while (reader.Read())
{
int resultInt32 = (int)reader.GetDecimal(0);
Assert.AreEqual(sampleInt32, resultInt32);
}
}
}
//Test 2 - NVarchar
using (OracleCommand commandNVarchar = connection.CreateCommand())
{
commandNVarchar.CommandText = sqlCommandtext;
commandNVarchar.Parameters.Add("PARAM", OracleDbType.NVarchar2, sampleNvarchar, ParameterDirection.Input);
using (IDataReader reader = commandNVarchar.ExecuteReader())
{
while (reader.Read())
{
string resultNVarchar = reader.GetString(0);
Assert.AreEqual(sampleNvarchar, resultNVarchar);
}
}
}
}
If commandInt32 is executed before commandNVarchar, execution of commandNVarchar fails with ORA-01722 - Invalid number. If the order is switched so commandNVarchar is executed first, it fails with "Specified cast is not valid" on reader.GetDecimal.
So far I've tried setting StatementCacheSize=0; Pooling=false; StatementCachePurge=true as ConnectionString parameters but I can't get this to work.
Is there anything I'm missing or are there any other options worth trying?
EDIT: Maybe some background on why this is needed/required: We don't use ODP or any other Dataprovider directly in our application (or at least: we're on our way to reach this goal), there's an DataLayer in between that performs database/provider specific optimiziations and monitoring of connection health,...
In this Layer for example StoredProcedures can be called, having the option of parameter type tuning. Some of our procedures have Clobs as Parameter types, as sometimes the value can be longer than x characters, but most likely it will be shorter.
So before executing via ExecuteNonQuery with ArrayBindCount set to y, parameter values are checked if Clob can be passed as varchar (Nclob as Nvarchar). "Rebinding" reduces the time to execute 2500 records from about 500ms to 200ms at the cost of losing a few ms checking string lengths. And this rebinding can only be done if the parameter type can be changed. Without this option we would need to execute it as Clob everytime, taking the performance hit.
To my understanding, parameter binding is unsupported in a SELECT list. I was so surprised that this worked at all that I had to run your code to see it with my own eyes. I believe that for the client to allow that SQL statement to execute at all is a bug.
Regardless, I inserted the following line between the test cases to get them both to work:
connection.PurgeStatementCache();
However, this only seems to work with the Managed Client (I've tried it with version 4.112.3.60). The regular client still fails as you describe.
Two things. When used as connection string parameters, the configuration variables need to have spaces, ie
Statement Cache Size=0;
The format you are using can be used directly in the config though:
http://docs.oracle.com/html/E10927_01/featConfig.htm#CJABCACG
http://docs.oracle.com/html/E10927_01/featOraCommand.htm#CIHCAFIG
You could use that same configuration section to enable tracing - comparing the traces might give you an idea of what is happening.
I believe PurgeStatementCache (not sure StatementCachePurge exists) is a runtime command, ie
connection.PurgeStatementCache
Metadata Pooling = false;
Our application is using Oracle 12c with ODP.Net Managed Provider
When using OracleCommandBuilder.DeriveParameters() we were always seeing the same parameters return from the stored procedure despite adding/ removing/ updating parameters. We would only see the changes after restarting the IIS process.
The only solution that worked was setting Metadata Pooling = false; in the Oracle connection string
We had no success with the following which have been mentioned here or on Oracle's forums:
connection.PurgeStatementCache();
Statement Cache Size=0;
Pooling = false;
What version of Oracle are you connecting to? This may be a bind variable peaking (or lack thereof) issue. The feature was introduced in 9i but had some issues all the way thru 10. You could try executing the following to see if you can reproduce the problem without ODP.net:
var param varchar2(255)
exec :param:='TEST';
select :param FROM DUAL;
change the type on "param" from varchar2 to number and change the value and reexecute to see what happens.
You could also try executing the command under a different connection instead of a shared one.
In the end, you could simply rename the bind variable in the statement, relative to the type (ie :paramNum or :paramString). The name you give the the parameter on the .net side is irrelevant unless cmd.BindByName is set to true. By default it is false, and variables are bound in the order they are added.

Using DateTime in a SqlParameter for Stored Procedure, format error

I'm trying to call a stored procedure (on a SQL 2005 server) from C#, .NET 2.0 using DateTime as a value to a SqlParameter. The SQL type in the stored procedure is 'datetime'.
Executing the sproc from SQL Management Studio works fine. But everytime I call it from C# I get an error about the date format.
When I run SQL Profiler to watch the calls, I then copy paste the exec call to see what's going on. These are my observations and notes about what I've attempted:
1) If I pass the DateTime in directly as a DateTime or converted to SqlDateTime, the field is surrounding by a PAIR of single quotes, such as
#Date_Of_Birth=N''1/8/2009 8:06:17 PM''
2) If I pass the DateTime in as a string, I only get the single quotes
3) Using SqlDateTime.ToSqlString() does not result in a UTC formatted datetime string (even after converting to universal time)
4) Using DateTime.ToString() does not result in a UTC formatted datetime string.
5) Manually setting the DbType for the SqlParameter to DateTime does not change the above observations.
So, my questions then, is how on earth do I get C# to pass the properly formatted time in the SqlParameter? Surely this is a common use case, why is it so difficult to get working? I can't seem to convert DateTime to a string that is SQL compatable (e.g. '2009-01-08T08:22:45')
EDIT
RE: BFree, the code to actually execute the sproc is as follows:
using (SqlCommand sprocCommand = new SqlCommand(sprocName))
{
sprocCommand.Connection = transaction.Connection;
sprocCommand.Transaction = transaction;
sprocCommand.CommandType = System.Data.CommandType.StoredProcedure;
sprocCommand.Parameters.AddRange(parameters.ToArray());
sprocCommand.ExecuteNonQuery();
}
To go into more detail about what I have tried:
parameters.Add(new SqlParameter("#Date_Of_Birth", DOB));
parameters.Add(new SqlParameter("#Date_Of_Birth", DOB.ToUniversalTime()));
parameters.Add(new SqlParameter("#Date_Of_Birth",
DOB.ToUniversalTime().ToString()));
SqlParameter param = new SqlParameter("#Date_Of_Birth",
System.Data.SqlDbType.DateTime);
param.Value = DOB.ToUniversalTime();
parameters.Add(param);
SqlParameter param = new SqlParameter("#Date_Of_Birth",
SqlDbType.DateTime);
param.Value = new SqlDateTime(DOB.ToUniversalTime());
parameters.Add(param);
parameters.Add(new SqlParameter("#Date_Of_Birth",
new SqlDateTime(DOB.ToUniversalTime()).ToSqlString()));
Additional EDIT
The one I thought most likely to work:
SqlParameter param = new SqlParameter("#Date_Of_Birth",
System.Data.SqlDbType.DateTime);
param.Value = DOB;
Results in this value in the exec call as seen in the SQL Profiler
#Date_Of_Birth=''2009-01-08 15:08:21:813''
If I modify this to be:
#Date_Of_Birth='2009-01-08T15:08:21'
It works, but it won't parse with pair of single quotes, and it wont convert to a DateTime correctly with the space between the date and time and with the milliseconds on the end.
Update and Success
I had copy/pasted the code above after the request from below. I trimmed things here and there to be concise. Turns out my problem was in the code I left out, which I'm sure any one of you would have spotted in an instant. I had wrapped my sproc calls inside a transaction. Turns out that I was simply not doing transaction.Commit()!!!!! I'm ashamed to say it, but there you have it.
I still don't know what's going on with the syntax I get back from the profiler. A coworker watched with his own instance of the profiler from his computer, and it returned proper syntax. Watching the very SAME executions from my profiler showed the incorrect syntax. It acted as a red-herring, making me believe there was a query syntax problem instead of the much more simple and true answer, which was that I need to commit the transaction!
I marked an answer below as correct, and threw in some up-votes on others because they did, after all, answer the question, even if they didn't fix my specific (brain lapse) issue.
How are you setting up the SqlParameter? You should set the SqlDbType property to SqlDbType.DateTime and then pass the DateTime directly to the parameter (do NOT convert to a string, you are asking for a bunch of problems then).
You should be able to get the value into the DB. If not, here is a very simple example of how to do it:
static void Main(string[] args)
{
// Create the connection.
using (SqlConnection connection = new SqlConnection(#"Data Source=..."))
{
// Open the connection.
connection.Open();
// Create the command.
using (SqlCommand command = new SqlCommand("xsp_Test", connection))
{
// Set the command type.
command.CommandType = System.Data.CommandType.StoredProcedure;
// Add the parameter.
SqlParameter parameter = command.Parameters.Add("#dt",
System.Data.SqlDbType.DateTime);
// Set the value.
parameter.Value = DateTime.Now;
// Make the call.
command.ExecuteNonQuery();
}
}
}
I think part of the issue here is that you are worried that the fact that the time is in UTC is not being conveyed to SQL Server. To that end, you shouldn't, because SQL Server doesn't know that a particular time is in a particular locale/time zone.
If you want to store the UTC value, then convert it to UTC before passing it to SQL Server (unless your server has the same time zone as the client code generating the DateTime, and even then, that's a risk, IMO). SQL Server will store this value and when you get it back, if you want to display it in local time, you have to do it yourself (which the DateTime struct will easily do).
All that being said, if you perform the conversion and then pass the converted UTC date (the date that is obtained by calling the ToUniversalTime method, not by converting to a string) to the stored procedure.
And when you get the value back, call the ToLocalTime method to get the time in the local time zone.
Here is how I add parameters:
sprocCommand.Parameters.Add(New SqlParameter("#Date_Of_Birth",Data.SqlDbType.DateTime))
sprocCommand.Parameters("#Date_Of_Birth").Value = DOB
I am assuming when you write out DOB there are no quotes.
Are you using a third-party control to get the date? I have had problems with the way the text value is generated from some of them.
Lastly, does it work if you type in the .Value attribute of the parameter without referencing DOB?
Just use:
param.AddWithValue("#Date_Of_Birth",DOB);
That will take care of all your problems.
If you use Microsoft.ApplicationBlocks.Data it'll make calling your sprocs a single line
SqlHelper.ExecuteNonQuery(ConnectionString, "SprocName", DOB)
Oh and I think casperOne is correct...if you want to ensure the correct datetime over multiple timezones then simply convert the value to UTC before you send the value to SQL Server
SqlHelper.ExecuteNonQuery(ConnectionString, "SprocName", DOB.ToUniversalTime())

Categories