全部,我正在编写一个应用程序,它通过 C#
中的 .NET Entity Framework
连接到 Oracle
服务器。我能够很好地插入数据。
此外,我能够很好地查询大多数数据。我看到的异常来自 Oracle
中的 NUMBER
或 FLOAT
被转换为 .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/