-
Notifications
You must be signed in to change notification settings - Fork 934
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid InvalidCastException with Oracle number high precision values #2641
Comments
Looks like the issue with the provider. |
Meaning there's nothing I can do from an application perspective that can prevent problematic values from getting inserted into the dB? |
@hazzik I discovered that using GetDouble() allows the value to be returned successfully, as opposed to the GetDecimal() method which gets called from within GetValue(). The property which is having a problem is a nullable double. Is there something about my use of a nullable double that is causing NHibernate to call GetValue() instead of GetDouble(), or does NHibernate use GetValue() across the board?
Applying precision and scale doesn't seem to make any difference in how the number is recorded to the dB. |
As you are using Oracle 11, you can enable binary floating point types, by setting configuration setting |
@maca88 Thank you for the suggestion! I tried it out but now I'm seeing InvalidCastExceptions at the same query but across the board for all numeric (double) values. The registration that happens in Oracle10gDialect.RegisterFloatingPointTypeMappings() is applied at data intake, correct? Or is this something I would have had to have enabled before any data was inserted into the dB in order to avoid compatibility issues? |
@maca88 I managed to confirm that the data inserted is slightly different when OracleUseBinaryFloatingPointTypes is on versus off ... surprisingly (to me, anyway), the precision seems to be increased when the setting is true. OracleUseBinaryFloatingPointTypes = false: OracleUseBinaryFloatingPointTypes = true In either case, GetDecimal() is still being called internally (same stack trace as above). I agree with @hazzik that this seems to be a bug with the GetValue() method on the provider; but is there anything I can do to get NHibernate to call GetDouble() instead of GetValue()? I did happen to come across this; the final response might provide a workaround, although his implementation isn't working out of the box for me, so I'll have to figure out how to enable it. |
Yes, but is it is also used to create columns of type
That is because you most likely didn't change the column type to |
@maca88 Thanks. I can look into changing the column types; the challenge is that I'm using Fluent Migrator to generate the dB, and it appears to be using double precision instead of binary_double to generate the columns. But that's a feature request for them. :) I just came across this about the same time that I received the response below from Oracle support. In ODP.NET 19.10, we added a new property to OracleDataReader and OracleDataAdapter, SuppressGetDecimalInvalidCastException, for this exact use case. Set the property value to true and it will round off the NUMBER value and suppress getting the exception. Is this a configuration setting that could be added to NHibernate? It seems like this would solve my problem very easily. |
Currently NHibernate doesn't have any interceptor for public class WrappedCommand : DbCommand
{
private OracleCommand _command;
public WrappedCommand(OracleCommand command)
{
_command = command;
}
protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
{
var reader = (OracleDataReader) await _command.ExecuteReaderAsync(behavior, cancellationToken);
reader.SuppressGetDecimalInvalidCastException = true;
return reader;
}
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
{
var reader = _command.ExecuteReader(behavior);
reader.SuppressGetDecimalInvalidCastException = true;
return reader;
}
// TODO: wrap missing methods
}
public class CustomDriver : OracleManagedDataClientDriver
{
public override DbCommand CreateCommand()
{
return new WrappedCommand((OracleCommand) base.CreateCommand());
}
public override DbCommand GenerateCommand(CommandType type, SqlString sqlString, SqlType[] parameterTypes)
{
return new WrappedCommand((OracleCommand) base.GenerateCommand(type, sqlString, parameterTypes));
}
} |
Got it - thanks for the tip! I enabled SuppressGetDecimalInvalidCastException in my custom query routine and it had absolutely no observable effect. I've sent a note to Oracle inquiring about it. I'll try out your method once I understand what either they or I are doing wrong. :) |
@HakanKaraoglu Sorry for the slow response. I did contact Oracle about it; they acknowledged it was a bug and committed to fix it in an upcoming version. However, I ended up putting in custom logic to limit the precision on values going to the dB since I couldn't wait for Oracle's release schedule and lost track of it after that. I would be curious to know whether a version of Oracle.ManagedDataAccess more recent than 19.10 resolves the issue, in case you find yourself checking it out. |
hello @kriewall, |
@maca88 I need to revive this conversation. I put in a local patch for the issue I faced a couple years ago, but I just ran into another problem pertaining to this behavior with the Oracle client. I've taken another look at the fix you recommended, but I'm continually running into
Once I did this, I started to see
... but that yields a So, I have a number of questions:
|
I wonder if we should revert this change from v5.1. As written in the #1607 comments, it was done because:
But since the Oracle driver is quite bad at reading them back, this was likely bad move. (But those Oracle driver failures to read them bask is in my opinion a defect that ideally should be fixed on their side, but it seems to take quite some time to happen.) So, reverting this v5.1 change may avoid having those "too high precision" non binary doubles inserted. Would you be able to check this with a custom dialect reverting that change, or by testing with NHibernate 5.0.8? |
We have forgotten to update the html documentation about this new setting. If we revert the 5.1 change I am pinpointing above, we should think about documenting this setting here by the way. |
On second thought, no, it will not change anything. It would only change the Oracle declared type when also setting in your mapping a precision on a double higher than the maximum set in the dialect. |
As far as I know, there is no other straightforward solution for this. You need to create a custom batcher that will unwrap the command before setting
As
Just by setting the mapping is not enough, you also need to change the column in the database in order it to work.
I think that a simpler solution would be to add a setting (similar to |
Thank you so much for the speedy responses! A simple property setting would be wonderful. I will wait for your more elegant solution since this issue doesn't occur that often and I can manage it manually when it does. If something comes up and the feature will be delayed for more than, say, a month, please let me know and I'll rethink my decision. |
Hi @maca88 @fredericDelaporte I apologize but I've installed NHibernate 5.3.11 and I'm not seeing |
That is not in 5.3.11, this is a "next minor" feature (likely 5.4), not yet published. |
I updated the test that was added for this issue by:
and I was still not able to get the |
Just a quick update: I downloaded the source code but first was getting a slew of ambiguous call errors on One difference I note is that I'm using
I have requested my counterpart to set up the dummy project you requested; hopefully we can get that to you soon. |
Yes, you need to modify the
with:
and change the |
@maca88 Okay, your code is fine; there was a developer error on our side. I've verified that the InvalidCastExceptions are getting suppressed in our application. Thanks again for taking the time to support this request - it's a huge relief to have this issue under control. |
About running the tests, as explained in contributing, use the build menu. It helps configuring your database. Once configured, you can run the tests with whatever NUnit compatible tool you like, including VS Test Explorer. (You are not restricted to using the build menu for running the tests.) |
@fredericDelaporte Thanks for the guidance, I'll make note of it for the next time. I was able to get the unit tests of interest running using @maca88's info; they do indeed pass when the suppression flag is enabled and fail for InvalidCastException when disabled. |
Describe the issue
I'm getting an
InvalidCastException
when rehydrating numbers with very small / high precision values in an Oracle dB. The limitation appears to actually be due to the fact that Oracle can represent numbers with higher precision than C# (described here); however my main question here is how to prevent NHibernate from inserting values into the dB that have higher precision than C# can handle. If you have recommendations how to work around the intake issue, that would be much appreciated as well.Expected behavior
As a workaround to prevent overly precise data from getting inserted into the dB, do I need to specify precision and scale everywhere I map a double? Is there a generic way to do this?
Environment
Additional information
Stack trace:
SessionFactory config
I have a separate query routine that uses the Oracle.ManagedDataAccess dll that was also bombing out for an InvalidCastException when used against similar data. I verified using the query that the issue resolved when I changed the value of the problematic number at the last decimal place). I'm still trying to figure out the exact source of the problem under NHibernate, but the signature is basically the same
InvalidCastException at GetDecimal()
.Example value that causes cast exception:
0.00000000000000422030887989616
Let me know if I can provide any additional information. Appreciate any help you can provide; I'm looking at a pretty ugly issue if I don't get this resolved soon.
The text was updated successfully, but these errors were encountered: