Can you change the Scaffolding mappings in EF Core? — oracle-tech

    Forum Stats

  • 3,715,496 Users
  • 2,242,776 Discussions
  • 7,845,366 Comments

Discussions

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Can you change the Scaffolding mappings in EF Core?

Tridus
Tridus Member Posts: 594 Silver Badge

Hi. In ODP.net EF, you can change the default mappings in the configuration before scaffolding. Is this still possible in core?

I ask because NUMBER(2) to NUMBER(4)maps to byte, which is unusable with any number over 255. Normally I just map these to short in configuration and the problem goes away, but I'm not sure how to do this in Core as the documentation doesn't mention it except editing the generated classes after the fact.

Thanks.

Best Answer

Answers

  • Alex Keh-Oracle
    Alex Keh-Oracle Posts: 2,751 Employee

    Yes, you can use Fluent API to change the default data type mapping.

  • Tridus
    Tridus Member Posts: 594 Silver Badge

    Is there an example of that somewhere? Is that something I can do so that it's generated correctly when I scaffold it via Scaffold-DBScaffold-DbContext? That's the part I'm having an issue with.

    Thanks Alex. :)

  • Alex Keh-Oracle
    Alex Keh-Oracle Posts: 2,751 Employee
    edited November 2020

    Here's a general tutorial for using Fluent API. The "Column Data Types" section shows how to customize the data type mapping. You just indicate the Oracle DB type you want to map to.

  • Tridus
    Tridus Member Posts: 594 Silver Badge

    I don't see how that solves the problem though? Scaffold-DbContext generated a byte. When I run it again to pick up new database structure changes, it's going to recreate it as a byte.

    That's the problem I'm having. A NUMBER(4,0) column comes out as a byte, and as soon as it reads a row with a value like 1234, it fails. I can change the generated classes after the fact, but those changes get wiped out if I run Scaffold-DbContext to recreate from the database again. In the EF6 version, the configuration mapping changes are picked up during this and it would create it as a short instead, which works.

    Sorry, but I'm really not understanding what I'd do with the fluent API that would help solve that problem.

  • Tridus
    Tridus Member Posts: 594 Silver Badge

    Hi Alex. So I tried that, and now saving with the column that has values incompatible with byte gives me the exception below. It doesn't actually matter what's in the column (including null), it simply doesn't work properly. Running it as a byte also isn't an option, because as a NUMBER(4,0), it easily has values that overflow byte.

    It's defined in EF as follows:

        public short? PlaceCd { get; set; }

          modelBuilder.Entity<TempAddress>(entity =>

          {

    entity.HasIndex(e => e.PlaceCd)

              .HasName("XIF5TEMP_ADDRESS");

    entity.Property(e => e.PlaceCd).HasColumnName("PLACE_CD");

    }

    I can't be sure this is the one that's causing the error as the error doesn't specify which column is the problem, but this is the one that overflows if I use the default mappings.


    System.Reflection.TargetInvocationException

     HResult=0x80131604

     Message=Exception has been thrown by the target of an invocation.

     Source=System.Private.CoreLib

     StackTrace:

      at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)

      at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

      at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)

      at Microsoft.EntityFrameworkCore.Update.Internal.KeyValueIndexFactorySource.Create(IKey key)

      at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)

      at Microsoft.EntityFrameworkCore.Update.Internal.KeyValueIndexFactorySource.GetKeyValueIndexFactory(IKey key)

      at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.AddForeignKeyEdges(Multigraph`2 commandGraph, Dictionary`2 predecessorsMap)

      at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.TopologicalSort(IEnumerable`1 commands)

      at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.<BatchCommands>d__11.MoveNext()

      at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)

      at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IList`1 entries)

      at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList`1 entriesToSave)

      at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(DbContext _, Boolean acceptAllChangesOnSuccess)

      at Oracle.EntityFrameworkCore.Storage.Internal.OracleExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)

      at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)

      at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)

      at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()

      at GNB.ELG.WellVoucher.Website.External.Controllers.VoucherController.Create(VoucherEditVm vm) in C:\workarea\GWIMS\Dev\GNB.ELG.WellVoucher\GNB.ELG.WellVoucher.Website.External\Controllers\VoucherController.cs:line 108

      at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)

      at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)

      at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()

      at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)

      at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()


    Inner Exception 1:

    InvalidCastException: Unable to cast object of type 'System.Func`3[System.Byte,System.Byte,System.Boolean]' to type 'System.Func`3[System.Int16,System.Int16,System.Boolean]'.

      at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.SimplePrincipalKeyValueFactory`1.NoNullsCustomEqualityComparer..ctor(ValueComparer comparer)

      at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.SimplePrincipalKeyValueFactory`1..ctor(IProperty property)

      at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.KeyValueFactoryFactory.CreateSimpleFactory[TKey](IKey key)

      at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.KeyValueFactoryFactory.Create[TKey](IKey key)

      at Microsoft.EntityFrameworkCore.Metadata.Internal.Key.<>c__19`1.<GetPrincipalKeyValueFactory>b__19_0(Key k)

      at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)

      at Microsoft.EntityFrameworkCore.Metadata.Internal.Key.GetPrincipalKeyValueFactory[TKey]()

      at Microsoft.EntityFrameworkCore.Metadata.Internal.KeyExtensions.GetPrincipalKeyValueFactory[TKey](IKey key)

      at Microsoft.EntityFrameworkCore.Update.Internal.KeyValueIndexFactorySource.CreateFactory[TKey](IKey key)

  • Tridus
    Tridus Member Posts: 594 Silver Badge

    So as an update on this, I'm not sure what happened, but I was trying to create an example case of this to demonstrate the problem in a smaller bit of code, and it worked fine.

    Then I updated the packages in my project and tried again... and it worked fine.

    I'm not sure what changed, but it seems fine now. Thanks Alex.

  • Alex Keh-Oracle
    Alex Keh-Oracle Posts: 2,751 Employee

    No problem! I was perplexed why you saw an exception. I'm glad to hear it's now working for you.

Sign In or Register to comment.