简体   繁体   English

Oracle 实体框架“指定的强制转换无效” GetDecimal

[英]Oracle Entity Framework 'Specified cast is not valid' GetDecimal

All, I am writing an application that is connecting to an Oracle server via the .NET Entity Framework in C# .所有,我正在编写一个通过C#.NET Entity Framework连接到Oracle服务器的应用程序。 I am able to insert data just fine.我能够插入数据就好了。 Also, I am able to query most data just fine.此外,我能够很好地查询大多数数据。 The exception I am seeing comes from a NUMBER or FLOAT in Oracle being converted to the .NET type decimal .我看到的异常来自OracleNUMBERFLOAT被转换为.NET类型decimal But it doesn't always happen.但它并不总是发生。

If I have this number in Oracle "0.96511627906976744186046511627906976744" then I get the exception.如果我在 Oracle 中有这个数字“0.96511627906976744186046511627906976744”,那么我会得到异常。 Not during the LINQ query, but when I execute a foreach with the queried data.不是在LINQ查询期间,而是在我使用查询数据执行foreach时。 But, If I have 0.9651162790 in the cell the query works fine.但是,如果我在单元格中有 0.9651162790 查询工作正常。

Is Oracle's Entity Framework not supposed to reduce the precision to that of a decimal type? Oracle 的Entity Framework不应该将精度降低到decimal类型的精度吗?

Stripping precision off the hundreds of millions of entries in our database obviously is not an option.从我们数据库中的数亿个条目中去除精度显然不是一种选择。

Here is some code:这是一些代码:

 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);
     }
 }

The Model:该模型:

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

Exception Details:异常详情:

Message: Specified cast is not valid.消息:指定的演员表无效。 Source: Oracle.ManagedDataAccess InnerException: null TargetSite: {System.Decimal GetDecimal(Int32)}来源: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.But在 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) 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) 在 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) in C:\\Users\\REMOVED\\Desktop\\Oracle Example\\Oracle Example\\Form1.cs:line 33 at System.Windows.Forms.Control.OnClick(EventArgs e) 在 System.Windows.Forms.Button.OnClick(EventArgs e) 在 System.Windows.Forms.But ton.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, ApplicationC ton.OnMouseUp(MouseEventArgs mevent) 在 System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks) 在 System.Windows.Forms.Control.WndProc(Message& m) 在 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) ) 在 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) 在 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg) 在 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 原因, ApplicationC ontext 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() ontext 上下文)在 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 (对象状态) 在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 在 System.Threading .ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 在 System.Threading.ThreadHelper.ThreadStart()

Update更新

I tried both of these.我试过这两种。 Neither work.都不工作。 And I tried them with really low numbers also.我也用非常低的数字尝试了它们。

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);
}

Update (new question) Does anyone know if it is possible to intercept these objects and truncate them before they are placed into the model?更新(新问题)有谁知道是否可以在将这些对象放入模型之前截取它们并截断它们? I feel like I should be able to override something to make this happen, but don't even know where to start looking.我觉得我应该能够覆盖一些东西来实现这一点,但甚至不知道从哪里开始寻找。

You would receive the same error using ADO .Net because the problem is in the ADO .Net provider (EF, during "materialization process" find that the target property is a decimal so it calls GetDecimal of the Oracle data reader).您会在使用 ADO .Net 时收到相同的错误,因为问题出在 ADO .Net 提供程序中(EF,在“具体化过程”期间发现目标属性是十进制数,因此它调用 Oracle 数据读取器的 GetDecimal)。

To understand while it happens only sometimes, you could have a look to the GetDecimal implementation.要了解它只是偶尔发生,您可以查看 GetDecimal 实现。 Probably Oracle implicitly convert a number only if the conversion can happen without precision loosing;可能只有当转换可以在不丢失精度的情况下发生时,Oracle 才会隐式转换数字; otherwise it does not convert the number and raise the error.否则它不会转换数字并引发错误。 Other providers simply does not convert not compatible types (so you get the error always not only on certain records).其他提供程序根本不会转换不兼容的类型(因此您总是不仅在某些记录上遇到错误)。

The best solution is probably to map the field to a double and use it.最好的解决方案可能是将字段映射到双精度并使用它。
You can also convert the double field to a decimal (if you want, you can do it in the same class with a non mapped field).您还可以将 double 字段转换为小数(如果需要,您可以在具有非映射字段的同一类中执行此操作)。 With this solution you won't be able to use the converted field in queries.使用此解决方案,您将无法在查询中使用转换后的字段。

EDIT编辑

I read the ADO.Net provider code and is quite strange.我阅读了 ADO.Net 提供程序代码并且很奇怪。
First of all, EF calls GetValue and is the Oracle ADO provider that calls GetDecimal.首先,EF调用GetValue,是调用GetDecimal的Oracle ADO提供者。 In the Oracle provider, Get_everythingwithapoint calls an inner GetDecimal function.在 Oracle 提供程序中,Get_everythingwithapoint 调用内部 GetDecimal 函数。 And I think that the problem is there so my solution won't work.我认为问题就在那里,所以我的解决方案不起作用。 I think that the only way is that Oracle fixes the ADO.Net provider.我认为唯一的方法是 Oracle 修复 ADO.Net 提供程序。

Just in case I paste here some code以防万一我在这里粘贴一些代码

from DataReader来自数据阅读器

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

from OracleColumn (the type of _columnInfo)来自 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;
    } 

See that GetDouble is the same as GetDecimal看到 GetDouble 和 GetDecimal 一样

You can also have a look to OracleNumber.MarshalToDecimal but I think that you'll conclude that you won't see it work also if you use ADO.Net.您还可以查看 OracleNumber.MarshalToDecimal,但我认为您会得出结论,如果您使用 ADO.Net,您也不会看到它工作。

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

cast (field as decimal(16,4)) as field 

I had a similar issue but with NHibernate as ORM.我有一个类似的问题,但使用 NHibernate 作为 ORM。 One of the column was NUMBER(*) .其中一列是NUMBER(*) For mapping I used FluentNHiberante and a simple extension method to use a custom formula to workaround the issue:对于映射,我使用了 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));
    }
}

There is now a general solution available with newer versions of Oracle.ManagedDataAccess version (I think version 20 and later):现在有一个通用解决方案可用于较新版本的 Oracle.ManagedDataAccess 版本(我认为版本 20 及更高版本):

Oracle introduced a boolean flag OracleDataReader.SuppressGetDecimalInvalidCastException . Oracle 引入了一个布尔标志OracleDataReader.SuppressGetDecimalInvalidCastException

In Entity Framework the problem is to get the instance of OracleDataReader, before the query is executed.在实体框架中,问题是在执行查询之前获取 OracleDataReader 的实例。 In EF Core you can use a CommandInterceptor to solve this:在 EF Core 中,您可以使用 CommandInterceptor 来解决这个问题:

public class OracleCommandInterceptor : DbCommandInterceptor
{
    private static OracleCommandInterceptor _Instance;

    public static OracleCommandInterceptor Instance
    {
        get
        {
            if (_Instance == null)
            {
                _Instance = new OracleCommandInterceptor();
            }
            return _Instance;
        }
    }

    public override DbDataReader ReaderExecuted(DbCommand command, CommandExecutedEventData eventData, DbDataReader result)
    {
        var reader = base.ReaderExecuted(command, eventData, result);

        if (reader is OracleDataReader oraReader)
        {
            oraReader.SuppressGetDecimalInvalidCastException = true;
        }

        return reader; 
    }

    public override Task<DbDataReader> ReaderExecutedAsync(DbCommand command, CommandExecutedEventData eventData, DbDataReader result, CancellationToken cancellationToken = default)
    {
        if (result is OracleDataReader oraReader)
        {
            oraReader.SuppressGetDecimalInvalidCastException = true;
        }
        return base.ReaderExecutedAsync(command, eventData, result, cancellationToken);
    }

}

In your DbContext class you have to add the Interceptor into the optionsbuilder:在您的 DbContext 类中,您必须将拦截器添加到选项构建器中:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    if (!optionsBuilder.IsConfigured)
    {
        ...
        optionsBuilder
            .UseOracle(connectionString)
            .AddInterceptors(OracleCommandInterceptor.Instance);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Oracle ManagedDataAccess GetDecimal 指定的转换无效 - Oracle ManagedDataAccess GetDecimal specified cast is not valid C#实体框架中的Oracle数据类型“指定的转换无效” - Oracle data type “Specified Cast is not valid” in C# Entity Framework Entity Framework Core 3.1 - Oracle “指定的转换无效” - Entity Framework Core 3.1 - Oracle “Specified cast is not valid” 指定的强制转换是无效的 C# 实体框架 - Specified cast is not valid C# Entity Framework 为什么在尝试使用Entity Framework执行查询时指定的强制转换无效? - Why is the specified cast not valid when trying to execute a query with Entity Framework? Oracle查询ATAN-指定的转换无效 - Oracle query for ATAN - Specified cast is not valid 实体框架Include():指定的包含路径无效 - Entity Framework Include() : A specified Include path is not valid Entity Framework SQLQuery Oracle 指定的方法不受支持 - Entity Framework SQLQuery Oracle specified method is not supported Oracle数据提供程序的异常行为(错误)(InvalidCastException:指定的转换无效) - Strange Behavior (Bug) of Oracle Data Provider (InvalidCastException: Specified Cast is not Valid) 保存LINQ-To-SQL实体时“指定的强制转换无效”错误 - “Specified cast is not valid” error when saving LINQ-To-SQL entity
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM