Oracle实体框架'指定演员表无效' GetDecimal

时间:2016-09-13 16:17:28

标签: c# oracle entity-framework

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

如果我在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; }
 }

异常详细信息:

  

消息:指定的强制转换无效。   来源:Oracle.ManagedDataAccess   InnerException:null   TargetSite:{System.Decimal GetDecimal(Int32)}

     

堆栈跟踪:

     

at Oracle.ManagedDataAccess.Client.OracleDataReader.GetDecimal(Int32 i)   在Oracle.ManagedDataAccess.Client.OracleDataReader.GetValue(Int32 i)   在System.Data.Entity.Core.Common.Internal.Materialization.Shaper.ErrorHandlingValueReader 1.GetUntypedValueDefault(DbDataReader reader,Int32 ordinal)   在System.Data.Entity.Core.Common.Internal.Materialization.Shaper.ErrorHandlingValueReader 1.GetValue(DbDataReader reader,Int32 ordinal)   在lambda_method(Closure,Shaper)   在System.Data.Entity.Core.Common.Internal.Materialization.Coordinator 1.ReadNextElement(Shaper shaper)   在System.Data.Entity.Core.Common.Internal.Materialization.Shaper 1.SimpleEnumerator.MoveNext()   在System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()   at Oracle_Example.Form1.button1_Click(Object sender,EventArgs e)在C:\ Users \ REMOVED \ Desktop \ Oracle示例\ Oracle示例\ Form1.cs:第33行   在System.Windows.Forms.Control.OnClick(EventArgs e)   在System.Windows.Forms.Button.OnClick(EventArgs e)   在System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)   在System.Windows.Forms.Control.WmMouseUp(消息&amp; m,MouseButtons按钮,Int32点击)   在System.Windows.Forms.Control.WndProc(消息&amp; m)   在System.Windows.Forms.ButtonBase.WndProc(消息&amp; m)   在System.Windows.Forms.Button.WndProc(消息&amp; m)   在System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message&amp; m)   在System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message&amp; m)   在System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd,Int32 msg,IntPtr wparam,IntPtr lparam)   在System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG&amp; msg)   在System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID,Int32 reason,Int32 pvLoopData)   在System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason,ApplicationContext context)   在System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason,ApplicationContext context)   在System.Windows.Forms.Application.Run(Form mainForm)   在C:\ Users \ REMOVED \ Desktop \ Oracle中的Oracle_Example.Program.Main()示例\ Oracle示例\ Program.cs:第19行   在System.AppDomain._nExecuteAssembly(RuntimeAssembly程序集,String [] args)   在System.AppDomain.ExecuteAssembly(String assemblyFile,Evidence assemblySecurity,String [] args)   在Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()   在System.Threading.ThreadHelper.ThreadStart_Context(对象状态)   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)   在System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback回调,对象状态)   在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);
}

更新(新问题) 有没有人知道是否有可能拦截这些对象并在它们被放入模型之前截断它们?我觉得我应该能够覆盖一些事情来实现这一目标,但我们甚至不知道从哪里开始寻找。

3 个答案:

答案 0 :(得分:2)

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

要了解它有时只发生,你可以查看GetDecimal实现。可能只有在转换可能发生而没有精确丢失的情况下,Oracle才会隐式转换数字;否则它不会转换数字并引发错误。其他提供程序根本不会转换不兼容的类型(因此您不仅会在某些记录上获得错误)。

最好的解决方案可能是将字段映射为双精度并使用它 您还可以将双字段转换为十进制(如果需要,可以在具有非映射字段的同一个类中执行此操作)。使用此解决方案,您无法在查询中使用转换后的字段。

修改

我阅读了ADO.Net提供商代码并且非常奇怪 首先,EF调用GetValue,并且是调用GetDecimal的Oracle ADO提供程序。在Oracle提供程序中,Get_everythingwithapoint调用内部GetDecimal函数。而且我认为问题就出现了,所以我的解决方案不会起作用。我认为唯一的办法就是Oracle修复了ADO.Net提供程序。

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

来自DataReader

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,您也不会看到它也能正常工作。

答案 1 :(得分:1)

Oracle十进制小数位数大于.net十进制数,因此,您最终可能会遇到十进制格式的例外情况,以克服需要首先在查询中强制转换字段的问题,这可以通过类似于

的方法来完成
cast (field as decimal(16,4)) as field 

答案 2 :(得分:0)

我有一个类似的问题,但是使用NHibernate作为ORM。列之一是NUMBER(*)。对于映射,我使用了FluentNHiberante和一个简单的扩展方法来使用自定义公式来解决该问题:

internal static class MappingExtensions {
    /// <summary>Specify the property formula to trunc up to 27 digits after comma</summary>
    /// <param name="columnName">Column name</param>
    /// <returns>This property mapping to continue the method chain</returns>
    internal static PropertyPart TruncDecimal(
        this PropertyPart propertyPart,
        string columnName
    ) {
        const string decimalCastFormula = "TRUNC({0},27)";

        return propertyPart
            .Formula(string.Format(castFormula, columnName));
    }
}