Oracle DateTime Reload Utc Problem

Posts   
 
    
Steria
User
Posts: 64
Joined: 26-Nov-2008
# Posted on: 01-Dec-2008 15:50:31   

Hi,

I'm using LLBLgen 2.6 Final with Oracle 10g. I insert all my dates in a utc format by the SaveEntity method on the adapter (the DateTime Kind property was set to UTC). I also precise that I want to reload my entities to get the primary key, and when the entity is loaded as well, the kind property has lost the utc value to Unspecified.

Is it possible to keep the kind value when I reload my object ?

Thanks in advance,

Best regards

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 01-Dec-2008 16:45:56   

A relevant Helpdesk thread discussed the same thing, and here are Frans' replies in that thread:

The problem is: you want to have a different mapping for "Timestamp with Local Timezone" for the ODP.NET types. This is defined in the driver. As you can understand, the default mapping is the right one: it uses the TimeStampLTZ type. However you want to use TimeStampTZ.

You can achieve that in a couple of ways. The best way IMHO is the one with the driver.

Download the latest SDK from the customer area. In there you'll find the sourcecode of all the drivers. You're using ODP.NET 10g, so you should open the project OracleDBDriver10g. remove all the other projects from the solution. We added BINARY_DOUBLE support on December 14th, so I'd recommend to you to update the runtime library to the latest build as well, as it had a change in the DQE to support this type and better support oracle types.

Ok, open the file OracleDBDriver.cs in the Oracle 10g driver. The mappings are table driven so it's easy to change. The one you want to change is at line 159: base.DBTypesAsProviderType[(int)OracleDbTypes.TimeStampWithLocalTimeZone] = OracleDbType.TimeStampLTZ.ToString();

You want to change that into: base.DBTypesAsProviderType[(int)OracleDbTypes.TimeStampWithLocalTimeZone] = OracleDbType.TimeStampTZ.ToString();

Recompile the driver (probably want to remove the key reference in the assembly info file) and place it in the Drivers\Oracle10g folder of the designer. Start the designer and load your project. Then re-generate the code. This should make any TimeStampLTZ field be using TimeStampTZ from now on. The values in the persistenceinfoprovider code are retrieved from this driver table.

The sourcecode is provided to you for these purposes, so you're free to alter the driver sourcecode to make it behave like your project requires.

Another way to make this work is a change in the Oracle DQE, though I think this change is the easiest

In the recent Oracle DQE (since december 14th) that routine has been removed, as it gave too many problems with BINARY_DOUBLE etc.: it was also not really helpful, as it actually did what OracleParameter does internally, so we've stripped it out.

THe downside of adding it to the DQE is that it won't be used with proc calls, if you want to use it there as well.

I think there's another way to do this, by using a template: you can use a different persistenceinfoprovider template by using a different templateID binding (so it comes down to create a copy of the template, alter it, and bind it to the same templateID in a different templatebindings file created with templatestudio. You then place that bindings file above the original one on the second tab in the generator configuration dialog. )

The template is persistenceInfoProviderAdapter.template in Templates\Oracle10gSpecific\Net2.x\C#

Add to the bottom of the PersistenceInfoProviderCore class: Code: private OracleDbType ConvertTimeZoneTypes(OracleDbType toCheck) { OracleDbType toReturn = toCheck; if(toCheck==OracleDbType.TimeStampLTZ) { toReturn = OracleDbType.TimeStampTZ; } return toReturn; }

Then at line 68 in the template, you change: OracleDbType.<[SourceColumnDbType]> with ConvertTimeZoneTypes(OracleDbType.<[SourceColumnDbType]>)

and you should be able to generate the change into your code without library changes. This code runs once, when the application starts.

The problem is: if we change it in the DQE, it will be changed for everyone, also for the people who DO want to use TimeStampLTZ. Therefore we think it's not wise to make that change to the DQE, as it affects everyone.

The latest runtime libs + source are from 23 january 2008.

Ideally, it would be best if there was some kind of mapping override feature in the designer, where you could specify for this particular project to use mapping ABC instead of XYZ. This is what we're considering in v3.

Steria
User
Posts: 64
Joined: 26-Nov-2008
# Posted on: 02-Dec-2008 08:55:14   

Hi,

Please could you join the fix to this post ?

Thanks in advance,

Best regards,

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 02-Dec-2008 09:02:17   

No fix was supplied, only instructions on how a user can customize this.

Steria
User
Posts: 64
Joined: 26-Nov-2008
# Posted on: 05-Dec-2008 18:58:16   

Hi,

I have tested the solution but it doesn't work anymore. I'm using the MSOracle driver. Do you have alternate solution for this one?

Thanks in advance,

Best regards,

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 06-Dec-2008 11:36:49   

Steria wrote:

Hi,

I have tested the solution but it doesn't work anymore. I'm using the MSOracle driver. Do you have alternate solution for this one?

Thanks in advance,

Best regards,

You really have to understand that we don't see your code nor do we see what the exact problem is nor do we know what you 'tested'. If you want us to help you, help us with more information.

That said, the quoted problem was for timezone specific datatypes. Do you use a timezone specific oracle datatype? You didn't say. You use MS Oracle. Be aware that this ado.net provider is limited compared to Oracle's own ODP.NET.

Frans Bouma | Lead developer LLBLGen Pro
Steria
User
Posts: 64
Joined: 26-Nov-2008
# Posted on: 06-Dec-2008 21:47:32   

Hi,

Thanks for your reply. But I also try to use the ODP .NET 10.2.x driver . I have added to the LLBLGen config the following:

<assemblyIdentity name="Oracle.DataAccess" publicKeyToken="89B483F429C47342"/> <bindingRedirect oldVersion="10.1.0.000-10.1.0.500" newVersion="10.2.0.100"/> </dependentAssembly>

I have generated the project as well. When I try to save a new entity I get the following error:

Could not load file or assembly 'Oracle.DataAccess, Version=2.102.2.20, Culture=neutral, PublicKeyToken=89b483f429c47342' or one of its dependencies. The system cannot find the file specified.

I have read a lot of articles about this problem but I don't found any solution to resolved it. I'm using an Oracle.DataAcess assembly version 10.2.0.100.

If I can use the odp .net, I hope also solved the problem for Utc date.

Could you help me ?

Thanks in advance

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 07-Dec-2008 18:43:08   
David Elizondo | LLBLGen Support Team
Steria
User
Posts: 64
Joined: 26-Nov-2008
# Posted on: 09-Dec-2008 11:51:09   

Ok thanks now it work fine. My driver was not installed properly.

Now I can test with the change of ODP .NET and adding the ConvertTimeZoneTypes method ( as describe bellow) in the the template. Create a new project and regenerate my entities then rebuild my application. In the PersistenceInfoProvider are present the changes made on the template.

But when I save my entities with the datetime (Kind=Utc) and reloaded it, I already have unspecified.

Here is the generate line in the persitence class:

base.AddElementFieldMapping("TestEntity", "TestTimestamp", "TestTimeStamp", false, (int)ConvertTimeZoneTypes(OracleDbType.TimeStamp), 11, 6, 0, false, "", null, typeof(System.DateTime), 0);

Best regards,

Walaa wrote:

A relevant Helpdesk thread discussed the same thing, and here are Frans' replies in that thread:

The problem is: you want to have a different mapping for "Timestamp with Local Timezone" for the ODP.NET types. This is defined in the driver. As you can understand, the default mapping is the right one: it uses the TimeStampLTZ type. However you want to use TimeStampTZ.

You can achieve that in a couple of ways. The best way IMHO is the one with the driver.

Download the latest SDK from the customer area. In there you'll find the sourcecode of all the drivers. You're using ODP.NET 10g, so you should open the project OracleDBDriver10g. remove all the other projects from the solution. We added BINARY_DOUBLE support on December 14th, so I'd recommend to you to update the runtime library to the latest build as well, as it had a change in the DQE to support this type and better support oracle types.

Ok, open the file OracleDBDriver.cs in the Oracle 10g driver. The mappings are table driven so it's easy to change. The one you want to change is at line 159: base.DBTypesAsProviderType[(int)OracleDbTypes.TimeStampWithLocalTimeZone] = OracleDbType.TimeStampLTZ.ToString();

You want to change that into: base.DBTypesAsProviderType[(int)OracleDbTypes.TimeStampWithLocalTimeZone] = OracleDbType.TimeStampTZ.ToString();

Recompile the driver (probably want to remove the key reference in the assembly info file) and place it in the Drivers\Oracle10g folder of the designer. Start the designer and load your project. Then re-generate the code. This should make any TimeStampLTZ field be using TimeStampTZ from now on. The values in the persistenceinfoprovider code are retrieved from this driver table.

The sourcecode is provided to you for these purposes, so you're free to alter the driver sourcecode to make it behave like your project requires.

Another way to make this work is a change in the Oracle DQE, though I think this change is the easiest

In the recent Oracle DQE (since december 14th) that routine has been removed, as it gave too many problems with BINARY_DOUBLE etc.: it was also not really helpful, as it actually did what OracleParameter does internally, so we've stripped it out.

THe downside of adding it to the DQE is that it won't be used with proc calls, if you want to use it there as well.

I think there's another way to do this, by using a template: you can use a different persistenceinfoprovider template by using a different templateID binding (so it comes down to create a copy of the template, alter it, and bind it to the same templateID in a different templatebindings file created with templatestudio. You then place that bindings file above the original one on the second tab in the generator configuration dialog. )

The template is persistenceInfoProviderAdapter.template in Templates\Oracle10gSpecific\Net2.x\C#

Add to the bottom of the PersistenceInfoProviderCore class: Code: private OracleDbType ConvertTimeZoneTypes(OracleDbType toCheck) { OracleDbType toReturn = toCheck; if(toCheck==OracleDbType.TimeStampLTZ) { toReturn = OracleDbType.TimeStampTZ; } return toReturn; }

Then at line 68 in the template, you change: OracleDbType.<[SourceColumnDbType]> with ConvertTimeZoneTypes(OracleDbType.<[SourceColumnDbType]>)

and you should be able to generate the change into your code without library changes. This code runs once, when the application starts.

The problem is: if we change it in the DQE, it will be changed for everyone, also for the people who DO want to use TimeStampLTZ. Therefore we think it's not wise to make that change to the DQE, as it affects everyone.

The latest runtime libs + source are from 23 january 2008.

Ideally, it would be best if there was some kind of mapping override feature in the designer, where you could specify for this particular project to use mapping ABC instead of XYZ. This is what we're considering in v3.

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 09-Dec-2008 11:56:22   

But when I save my entities with the datetime (Kind=Utc) and reloaded it, I already have unspecified.

Please examine the LLBLGen generated SQL query and try to run it against the database, and see if a valid value gets inserted or not.

Steria
User
Posts: 64
Joined: 26-Nov-2008
# Posted on: 09-Dec-2008 13:49:42   

Walaa wrote:

But when I save my entities with the datetime (Kind=Utc) and reloaded it, I already have unspecified.

Please examine the LLBLGen generated SQL query and try to run it against the database, and see if a valid value gets inserted or not.

Hi In both cases inserting timestamp in my database directly using generated sql statement or by the saveentity method my result is : 2008-12-09 12:00:01 +00:00.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 09-Dec-2008 16:23:09   

Steria wrote:

Walaa wrote:

But when I save my entities with the datetime (Kind=Utc) and reloaded it, I already have unspecified.

Please examine the LLBLGen generated SQL query and try to run it against the database, and see if a valid value gets inserted or not.

Hi In both cases inserting timestamp in my database directly using generated sql statement or by the saveentity method my result is : 2008-12-09 12:00:01 +00:00.

As I've told you before, we don't have your code in front of us. I still have a hard time understanding exactly what you're doing and why it fails.

+00 suggests that the time is correct, as you specified 'Utc'.

Frans Bouma | Lead developer LLBLGen Pro
Steria
User
Posts: 64
Joined: 26-Nov-2008
# Posted on: 09-Dec-2008 17:38:27   

Otis wrote:

Steria wrote:

Walaa wrote:

But when I save my entities with the datetime (Kind=Utc) and reloaded it, I already have unspecified.

Please examine the LLBLGen generated SQL query and try to run it against the database, and see if a valid value gets inserted or not.

Hi In both cases inserting timestamp in my database directly using generated sql statement or by the saveentity method my result is : 2008-12-09 12:00:01 +00:00.

As I've told you before, we don't have your code in front of us. I still have a hard time understanding exactly what you're doing and why it fails.

+00 suggests that the time is correct, as you specified 'Utc'.

Sorry I'm just thinking that the code was present. So here is the code :


            using (DataAccessAdapter adapter = new DataAccessAdapter())
            {
                TestTimestamp testEntity = new TestTimestamp();
                testEntity.TestTimestamp = new DateTime(2008, 12, 09, 12, 00, 01, 0, DateTimeKind.Utc);
                adapter.SaveEntity(testEntity, true);
            }

So the Datetime kind is set to utc. When I save my entity you will see that I request a reload and after it the kind is set to Unspecified.

Hope this can help you.

Best regards,

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 09-Dec-2008 18:24:28   

Please specify: - table DDL SQL - what extra info you've specified in the connection string to make oracle convert the time - did you make the changes to the DQE as well?

Frans Bouma | Lead developer LLBLGen Pro
Steria
User
Posts: 64
Joined: 26-Nov-2008
# Posted on: 09-Dec-2008 19:57:04   

Otis wrote:

Please specify: - table DDL SQL - what extra info you've specified in the connection string to make oracle convert the time - did you make the changes to the DQE as well?

What do you mean by table ddl sql ? I have the default configuration in the connection string : UserId Password, data source and persist security info = True. OracleAnsiJoins = true in the app settings.

I have perform the modification as well, and I can see these on the persitenceinfo class.

Best regards

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 09-Dec-2008 20:34:28   

Steria wrote:

Otis wrote:

Please specify: - table DDL SQL - what extra info you've specified in the connection string to make oracle convert the time - did you make the changes to the DQE as well?

What do you mean by table ddl sql ?

The CREATE TABLE statement so we see exactly what type you've defined. There are more timestamp types with timezone's simple_smile

I have the default configuration in the connection string : UserId Password, data source and persist security info = True. OracleAnsiJoins = true in the app settings.

The main point is: 'local timezone' is local to a given point. ODP.NET threats a parameter of local timezone as if it's created in the timezone of the ODP.NET client, but if you set the timezone of the connection / thread to a different time, it therefore won't work and you need to make the modifications specified earlier in the thread.

I get the feeling your problem is something else than what's discussed here. Therefore we need to know details, like if you use TimestampLTZ or TimestampTZ. Also, it's not certain that it's our fault as the value returned from a fetch is wrapped into a DateTime instance by ODP.NET. (so we don't have any influence over that).

Frans Bouma | Lead developer LLBLGen Pro
Steria
User
Posts: 64
Joined: 26-Nov-2008
# Posted on: 09-Dec-2008 20:58:46   

create table TestTimeStamp  (
   TestTimeStamp TIMESTAMP not null)

So the timestamp created have length value of 6

The main point is: 'local timezone' is local to a given point. ODP.NET threats a parameter of local timezone as if it's created in the timezone of the ODP.NET client, but if you set the timezone of the connection / thread to a different time, it therefore won't work and you need to make the modifications specified earlier in the thread.

How to make the necessary change ?

I get the feeling your problem is something else than what's discussed here. Therefore we need to know details, like if you use TimestampLTZ or TimestampTZ.

How can I supply to you this information?

Best regards,

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39859
Joined: 17-Aug-2003
# Posted on: 09-Dec-2008 21:25:09   

Steria wrote:


create table TestTimeStamp  (
   TestTimeStamp TIMESTAMP not null)

So the timestamp created have length value of 6

that's a type without timezone information. You have to use a timestamp type with timezone information like TIMESTAMP WITH TIME ZONE

In other words: your type is wrong, it doesn't store the timezone info.

Frans Bouma | Lead developer LLBLGen Pro
Steria
User
Posts: 64
Joined: 26-Nov-2008
# Posted on: 17-Feb-2009 11:13:34   

Hi,

I have change my table with your recommandations and now I can use the DateTime Utc. But I still have a problem. I'm using the SaveEntity(testTimeStamp , true), and when I try to access to a property of my testTimeStamp instance I receive an ORMException: The entity is out of sync with its data in the database. Refetch this entity before using this in-memory instance.

Could you help me ?

Thanks in advance,

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 17-Feb-2009 11:23:44   

But I still have a problem. I'm using the SaveEntity(testTimeStamp , true), and when I try to access to a property of my testTimeStamp instance I receive an ORMException: The entity is out of sync with its data in the database. Refetch this entity before using this in-memory instance.

That's a complete different issue, that should have its own thread.

Please open a new thread, post code snippet and the exception text (you already did), and the stack trace.

Thanks.