c# - Oracle Entity Framework 'Specified cast is not valid' GetDecimal

标签 c# oracle entity-framework

全部,我正在编写一个应用程序,它通过 C# 中的 .NET Entity Framework 连接到 Oracle 服务器。我能够很好地插入数据。 此外,我能够很好地查询大多数数据。我看到的异常来自 Oracle 中的 NUMBERFLOAT 被转换为 .NET 类型 十进制。但这并不总是发生。

如果我在 Oracle 中有这个数字“0.96511627906976744186046511627906976744”,那么我会得到异常。不是在 LINQ 查询期间,而是在我使用查询数据执行 foreach 时。但是,如果单元格中有 0.9651162790,则查询工作正常。

Oracle 的 Entity Framework 不应该将精度降低到 decimal 类型吗?

从我们数据库中数以亿计的条目中剥离精度显然不是一种选择。

这是一些代码:

 using (Entities context = new Entities())
 {
     var data = from row in context.YieldsTestWeeklies
                select new
                {
                    D2Yield = row.D2Yield
                };

     foreach (var row in data)
     {
         textBox1.AppendText(row.D2Yield.ToString() + Environment.NewLine);
     }
 }

模型:

 public partial class YieldsTestWeekly
 {
     public Nullable<decimal> D2Yield { get; set; }
 }

异常详情:

Message: Specified cast is not valid. Source: Oracle.ManagedDataAccess InnerException: null TargetSite: {System.Decimal GetDecimal(Int32)}

StackTrace:

at Oracle.ManagedDataAccess.Client.OracleDataReader.GetDecimal(Int32 i) at Oracle.ManagedDataAccess.Client.OracleDataReader.GetValue(Int32 i) at System.Data.Entity.Core.Common.Internal.Materialization.Shaper.ErrorHandlingValueReader 1.GetUntypedValueDefault(DbDataReader reader, Int32 ordinal) at System.Data.Entity.Core.Common.Internal.Materialization.Shaper.ErrorHandlingValueReader 1.GetValue(DbDataReader reader, Int32 ordinal) at lambda_method(Closure , Shaper ) at System.Data.Entity.Core.Common.Internal.Materialization.Coordinator 1.ReadNextElement(Shaper shaper) at System.Data.Entity.Core.Common.Internal.Materialization.Shaper 1.SimpleEnumerator.MoveNext() at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext() at Oracle_Example.Form1.button1_Click(Object sender, EventArgs e) in C:\Users\REMOVED\Desktop\Oracle Example\Oracle Example\Form1.cs:line 33 at System.Windows.Forms.Control.OnClick(EventArgs e) at System.Windows.Forms.Button.OnClick(EventArgs e) at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent) at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.ButtonBase.WndProc(Message& m) at System.Windows.Forms.Button.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg) at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData) at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context) at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context) at System.Windows.Forms.Application.Run(Form mainForm) at Oracle_Example.Program.Main() in C:\Users\REMOVED\Desktop\Oracle Example\Oracle Example\Program.cs:line 19 at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()

更新

这两个我都试过了。既不工作。我也用非常低的数字尝试了它们。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<DecimalPropertyConvention>();
    modelBuilder.Conventions.Add(new DecimalPropertyConvention(38, 18));
    base.OnModelCreating(modelBuilder);
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<YieldsTestWeekly>().Property(e => e.D2Yield).HasPrecision(38, 18);
    base.OnModelCreating(modelBuilder);
}

更新(新问题) 有谁知道是否可以拦截这些对象并在将它们放入模型之前截断它们?我觉得我应该能够覆盖一些东西来实现这一点,但我什至不知道从哪里开始寻找。

最佳答案

使用 ADO .Net 您会收到相同的错误,因为问题出在 ADO .Net 提供程序中(EF,在“实现过程”期间发现目标属性是小数,因此它调用 Oracle 数据读取器的 GetDecimal)。

虽然它只是有时发生,但要了解它,您可以查看 GetDecimal 实现。可能只有当转换可以在不丢失精度的情况下发生时,Oracle 才会隐式转换数字;否则它不会转换数字并引发错误。其他提供者根本不转换不兼容的类型(因此您总是不仅在某些记录上会收到错误)。

最好的解决方案可能是将字段映射到 double 并使用它。
您还可以将 double 字段转换为小数(如果需要,您可以在同一个类中使用非映射字段进行转换)。使用此解决方案,您将无法在查询中使用转换后的字段。

编辑

我阅读了 ADO.Net 提供程序代码,感到很奇怪。
首先,EF调用GetValue,是调用GetDecimal的Oracle ADO provider。在 Oracle 提供程序中,Get_everythingwithapoint 调用内部 GetDecimal 函数。而且我认为问题就在那里,所以我的解决方案行不通。我认为唯一的方法是 Oracle 修复 ADO.Net 提供程序。

以防万一我在这里粘贴一些代码

来自数据阅读器

override public decimal GetDecimal(int i) {
    AssertReaderIsOpen();
    AssertReaderHasData(); 
    return _columnInfo[i].GetDecimal(_buffer);
} 

来自 OracleColumn(_columnInfo 的类型)

    internal decimal GetDecimal(NativeBuffer_RowBuffer buffer) {
        if (typeof(decimal) != _metaType.BaseType) { 
            throw ADP.InvalidCast();
        }
        if (IsDBNull(buffer)) {
            throw ADP.DataReaderNoData(); 
        }
        Debug.Assert(null == _longBuffer, "dangling long buffer?"); 

        decimal result = OracleNumber.MarshalToDecimal(buffer, _valueOffset, _connection);
        return result; 
    }

    internal double GetDouble(NativeBuffer_RowBuffer buffer) {
        if (typeof(decimal) != _metaType.BaseType) { 
            throw ADP.InvalidCast();
        } 
        if (IsDBNull(buffer)) { 
            throw ADP.DataReaderNoData();
        } 
        Debug.Assert(null == _longBuffer, "dangling long buffer?");

        decimal decimalValue = OracleNumber.MarshalToDecimal(buffer, _valueOffset, _connection);
        double result = (double)decimalValue; 
        return result;
    } 

看到GetDouble和GetDecimal是一样的

您也可以查看 OracleNumber.MarshalToDecimal,但我认为您会得出结论,如果您使用 ADO.Net,您也不会看到它起作用。

关于c# - Oracle Entity Framework 'Specified cast is not valid' GetDecimal,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39474686/

相关文章:

c# - Ef Linq 查询超时,但相同的查询在 SSMS 上不到 1 秒

c# - 测试 Entity Framework 查找方法

mysql - Entity Framework 的多数据库支持

c# - WPF 中图像更新的 TargetInvocationException

c# - 通信对象 System.ServiceModel.Channels.ServiceChannel 不能用于通信,因为它处于 Faulted 状态

c# - 表单在 Hide() 上处理

sql - 从 Oracle 中的字符串中删除重音符号

performance - 如何在没有提示的情况下增加哈希连接、分组依据和排序依据的 Oracle CBO 成本估算

c# - 更新后的 MS Access 数据库无法正常工作

java - 使用 IN 和 OUT 参数扩展 Spring StoredProcedure