Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request: Support Custom Key Types (that can be converted to int) #10995

Closed
ardalis opened this issue Feb 16, 2018 · 2 comments
Closed

Request: Support Custom Key Types (that can be converted to int) #10995

ardalis opened this issue Feb 16, 2018 · 2 comments

Comments

@ardalis
Copy link

ardalis commented Feb 16, 2018

I'd like to be able to use a custom key type that can be assigned freely to/from an int (or another allowable type) as my entity ID type. Currently I get the following exception when I try to add a migration.

Exception message:
The property 'Cart.Id' could not be mapped, because it is of type 'CartKey' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Stack trace:
System.InvalidOperationException: The property 'Cart.Id' could not be mapped, because it is of type 'CartKey' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.PropertyMappingValidationConvention.Apply(InternalModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelBuilt(InternalModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelBuilt(InternalModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.Validate()
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalModelBuilder.Validate()
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.<>c__DisplayClass5_0.<GetModel>b__0(Object k)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_1(IServiceProvider p)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass22_0.<RealizeService>b__0(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)

Steps to reproduce

Create an entity with a custom typed ID property. Try to add a migration.

// Entity
    public class Cart
    {
        public CartKey Id { get; set; }
        //public Customer Customer { get; set; }
        //public List<CartItem> Items { get; set; }
    }

// Key Type
    [ImmutableObject(true)]
    public struct CartKey : IComparable<CartKey>
    {
        public CartKey(int value) { Value = value; }

        [Pure] public int Value { get; }
        [Pure] public bool Equals(CartKey other) => Value == other.Value;

        [Pure]
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (Equals(this, obj)) return true;
            return obj.GetType() == GetType() && Equals((CartKey)obj);
        }

        [Pure]
        public override int GetHashCode()
        {
            unchecked
            {
                return (base.GetHashCode() * 397) ^ Value;
            }
        }

        [Pure] public static bool operator ==(CartKey left, CartKey right) => Equals(left, right);
        [Pure] public static bool operator !=(CartKey left, CartKey right) => !Equals(left, right);
        [Pure] public int CompareTo(CartKey other) => Equals(this, other) ? 0 : Value.CompareTo(other.Value);
        [Pure] public static bool operator <(CartKey left, CartKey right) => Comparer<CartKey>.Default.Compare(left, right) < 0;
        [Pure] public static bool operator >(CartKey left, CartKey right) => Comparer<CartKey>.Default.Compare(left, right) > 0;
        [Pure] public static bool operator <=(CartKey left, CartKey right) => Comparer<CartKey>.Default.Compare(left, right) <= 0;
        [Pure] public static bool operator >=(CartKey left, CartKey right) => Comparer<CartKey>.Default.Compare(left, right) >= 0;
        [Pure] public override string ToString() => $"{nameof(Value)}: {Value}";
        [Pure] public static implicit operator int(CartKey value) { return value.Value;  }
    }

Further technical details

EF Core version: 2.1
Database Provider: (e.g. Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10
IDE: Visual Studio 2017 15.5.6

@ardalis
Copy link
Author

ardalis commented Feb 16, 2018

Related to issue #10994 which demonstrates how I tried to work around this issue by using an int backing field (but this, too, didn't work).

@ajcvickers
Copy link
Member

Also duplicate of #242.

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants