Ticket T1179042
Visible to All Users

EF Core - A custom reference type key (e.g. a String key) may throw InvalidOperationException (Unable to track an entity of type X because its primary key property Y is null) at IObjectSpace.CreateObject

created 2 years ago

Steps to reproduce:

  1. Create the following data model with a custom reference type key (not auto-generated):
C#
[DefaultProperty(nameof(Symbol))] public class VatRate { [FieldSize(3)] [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] public virtual string Symbol { get; set; } }
  1. Call the IObjectSpace.CreateObject method for this data model or create this data model via XAF's UI (the New Object Action):
C#
vat = ObjectSpace.CreateObject<VatRate>(); vat.Symbol = symbol;

Current results:

System.InvalidOperationException HResult=0x80131509 Message=Unable to track an entity of type 'VatRate' because its primary key property 'Symbol' is null. Source=Microsoft.EntityFrameworkCore StackTrace: at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NullableKeyIdentityMap`1.Add(InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges, Boolean modifyProperties) …

Expected results:

  1. A CreateObject method overload that accepts initialization logic, similar to what EF Core has.
  2. A way to override object creation via XAF's UI (for instance, via an event handler or a virtual method in the NewObjectViewController class).
  3. Our backend Web API Service also allows to create new VatRate objects via endpoints.

Workarounds

  1. Inheritance from our BaseObject or a custom auto-generated System.Guid or Int32/Int64 key can be a workaround. You can also create a unique index for your Symbol column in this case: https://learn.microsoft.com/en-us/ef/core/modeling/indexes?tabs=data-annotations#index-uniqueness.
  2. More complex workarounds include implementing a custom EFCoreObjectSpace.

Answers approved by DevExpress Support

created 2 years ago

Hello,

To generate a temporary and unique value for a key property, use Value Generators. This is a standard feature of EF Core.

The following example demonstrates the use of StringValueGenerator included in EF Core:

C#
public class TestObject { [Key] public virtual string Code { get; set; } public virtual string Name { get; set; } } public class DataContext : DbContext { public DbSet<TestObject> TestObjects { get; set; } // ... protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<TestObject>().Property(t => t.Code).HasValueGenerator<StringValueGenerator>(); } }

This Value Generator populates the Code property with a string representation of Guid.NewGuid() when an object is added to DbContext. You can also implement your own custom Value Generator class, which will implement the logic that will generate values for key properties.

    Comments (2)
    JK JK
    Jacek Kosinski 2 years ago

      i dont need generate unique value. I need assign known value as a key value i.e. for currency EUR, USD.

      Andrey K (DevExpress Support) 2 years ago

        Hello,

        The solution Maxim described allows you to assign your own keys to newly created records. For example:

        C#
        var obj = ObjectSpace.CreateObject<TestObject>(); obj.Code = "your_key_value";

        Please test this code and let me know if it helps.

        Thanks,
        Andrey

        Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

        Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.