2016-09-13 3 views
1

Все, я пишу приложение, которое подключается к серверу Oracle через .NET Entity Framework в C#. Я могу вставить данные просто отлично. Кроме того, я могу просто запросить большинство данных. Исключение, которое я вижу, исходит от NUMBER или FLOAT в Oracle, который преобразуется в .NET тип decimal. Но это не всегда происходит.Описанный приказ Oracle Entity Framework недействителен 'GetDecimal

Если у меня есть это число в Oracle "0.96511627906976744186046511627906976744", то я получаю исключение. Не во время запроса LINQ, но когда я выполняю foreach с запрошенными данными. Но, если у меня 0,96651162790 в ячейке, запрос работает нормально.

Является ли 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: нуль TargetSite: {System.Decimal GetDecimal (Int32)}

StackTrace:

на 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 порядковый номер) в System.Data.Entity.Core. Common.Internal.Materialization.Shaper.ErrorHandlingValueReader 1.GetValue (DbDataReader reader, Int32-ординал) at lambda_method (Закрытие, Шейпер) в System.Data.Entity.Core.Common.Internal.Materialization.Coordinator 1.ReadNextElement (формирователь шейдера) в System.Data.Entity.Core.Common.Internal.Materialization.Shaper 1 .SimpleEnumerator.MoveNext() в System.Data.Entity.Internal.LazyEnumerator`1.MoveNext() в Oracle_Example.Form1.button1_Click (отправитель объекта, EventArgs e) в C: \ Users \ REMOVED \ Desktop \ Oracle Example \ Пример 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 (сообщение & m, кнопка MouseButtons, клики Int32)на System.Windows.Forms.Control.WndProc (сообщение & м) в System.Windows.Forms.ButtonBase.WndProc (Message & м) на System.Windows.Forms.Button.WndProc (сообщение & м) на System.Windows.Forms.Control.ControlNativeWindow.OnMessage (Message & m) в System.Windows.Forms.Control.ControlNativeWindow.WndProc (Message & м) на System.Windows.Forms.NativeWindow.DebuggableCallback (IntPtr HWND, Int32 сообщ, IntPtr WPARAM, IntPtr LPARAM) в System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW (MSG & МВП) в системе. Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop (IntPtr dwComponentID, Int32 причина, Int32 pvLoopData) на System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner (Int32 причина, ApplicationContext контекст) в System.Windows.Forms.Application.ThreadContext.RunMessageLoop (причина Int32, контекст ApplicationContext) в System.Windows.Forms.Application.Run (Form mainForm) по адресу Oracle_Example.Program.Main() в C: \ Users \ REMOVE D \ Desktop \ Oracle Example \ Oracle Example \ 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 (состояние объекта) на System.Threading.ExecutionContext.RunInternal (ExecutionContext ExecutionContext, ContextCallback обратного вызова, состояние объекта, Boolean preserveSyncCtx) в System.Threading.ExecutionContext.Run (ExecutionContext executeContext, ContextCallback callback, состояние объекта, Boolean preserveSyncCtx) в System.Threading.ExecutionContext.Run (ВыполнениеКонтекстовый запускКонтекст, ContextCallback callback, Object stat е) в System.Threading.ThreadHelper.ThreadStart()

Update

Я попробовал оба из них. Ни одна из них не работает. И я попробовал их с очень низким числом.

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 (новый вопрос) Кто-нибудь знает, если это возможно, чтобы перехватить эти объекты и укоротить их, прежде чем они будут помещены в модель? Я чувствую, что должен уметь переоценивать что-то, чтобы это произошло, но даже не знаю, с чего начать.

+1

Вы не единственный - https://community.oracle.com/thread/3951568?start=0&tstart=0 –

ответ

1

Вы получили бы такую ​​же ошибку, используя ADO .Net, так как проблема в ADO .Net-провайдере (EF, во время процесса материализации) обнаруживает, что целевое свойство является десятичным, поэтому он вызывает GetDecimal для чтения данных Oracle) ,

Чтобы понять, когда это происходит только иногда, вы можете взглянуть на реализацию GetDecimal. Вероятно, Oracle неявно конвертирует число только в том случае, если преобразование может произойти без потери точности; в противном случае он не преобразует число и не вызывает ошибку. Другие поставщики просто не конвертируют несовместимые типы (так что вы всегда получаете ошибку не только для определенных записей).

Лучшее решение, вероятно, состоит в том, чтобы сопоставить поле с двойным и использовать его.
Вы также можете преобразовать двойное поле в десятичное (если хотите, вы можете сделать это в том же классе с не отображаемым полем). С помощью этого решения вы не сможете использовать преобразованное поле в запросах.

EDIT

Я прочитал код провайдера ADO.NET и довольно странно.
Прежде всего, EF вызывает GetValue и является провайдером Oracle ADO, который вызывает GetDecimal.В поставщике 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 bu Думаю, вы поймете, что вы не увидите, что это работает, если вы используете ADO.Net.

+0

Если нет способа конвертировать номер Oracle или float в десятичный код C#, то каков был что они даже добавили функциональность Entity Framework? Если я сопоставляю его с двойным, я теряю еще большую точность. И я не понимаю, как это все равно не сработает для двойника. – ozziwald

+0

Отредактирован ответ. Вероятно, это ошибка Oracle, и нет решения. Также с провайдером ADO.Net без использования EF – bubi

+0

Может быть связан https://chrismay.org/2015/07/30/bug-in-oracledatareader-causing-invalidcastexception/ –

Смежные вопросы