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

Bug: 'Only backing fields of types that are assignable from the property type can be used.' #10994

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

Comments

@ardalis
Copy link

ardalis commented Feb 16, 2018

I have a custom type that I want to use as an entity's primary key. This type can be implicitly converted to or assigned from an int, and I've defined in int backing field in an attempt to get this to work (since using the type directly results in a different error). I should be able to use it as a backing field for a type that can be converted to/from an int.

When attempting to add migrations using my model, I get the following exception:

Exception message:
Stack trace:

System.InvalidOperationException: The specified field '_id' of type 'int' cannot be used for the property 'Cart.Id' of type 'CartKey'. Only backing fields of types that are assignable from the property type can be used.
   at Microsoft.EntityFrameworkCore.Metadata.Internal.PropertyBase.IsCompatible(FieldInfo fieldInfo, Type propertyType, Type entityClrType, String propertyName, Boolean shouldThrow)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.PropertyBase.SetFieldInfo(FieldInfo fieldInfo, ConfigurationSource configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalPropertyBuilder.HasField(String fieldName, ConfigurationSource configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder.HasField(String fieldName)
   at Microsoft.EntityFrameworkCore.Metadata.Builders.PropertyBuilder`1.HasField(String fieldName)
   at CustomKeys.Data.ApplicationDbContext.<>c.<OnModelCreating>b__1_0(EntityTypeBuilder`1 cart) in c:\Users\steve_000\Source\Repos\CustomKeys\Data\ApplicationDbContext.cs:line 27
   at Microsoft.EntityFrameworkCore.ModelBuilder.Entity[TEntity](Action`1 buildAction)
   at CustomKeys.Data.ApplicationDbContext.OnModelCreating(ModelBuilder builder) in c:\Users\steve_000\Source\Repos\CustomKeys\Data\ApplicationDbContext.cs:line 25
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelCustomizer.Customize(ModelBuilder modelBuilder, DbContext context)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelCustomizer.Customize(ModelBuilder modelBuilder, DbContext context)
   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.ServiceProvider.<>c__DisplayClass22_0.<RealizeService>b__0(ServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_1.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The specified field '_id' of type 'int' cannot be used for the property 'Cart.Id' of type 'CartKey'. Only backing fields of types that are assignable from the property type can be used.

Steps to reproduce

Include a complete code listing (or project/solution) that we can run to reproduce the issue.

// custom 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;  }
    }

// entity
    public class Cart
    {
        private int _id;
        public CartKey Id
        {
            get
            {
                return new CartKey(_id);
            }
            set
            {
                _id = value;
            }
        }
        public Customer Customer { get; set; }
        public List<CartItem> Items { get; set; }
    }

// OnModelCreating
            builder.Entity<Cart>(cart =>
            {
                cart.Property(c => c.Id)
                    .HasField("_id");
                cart.HasKey(c => c.Id);
            });

This is in a standard File-New-ASP.NET Core Web Application with Individual User Accounts.

Further technical details

EF Core version: 2.0.1
Database Provider: SQL Server
Operating system:
IDE: Visual Studio 2017 15.5.6

@ajcvickers
Copy link
Member

This is supported by type conversions (#242) in 2.1. In 2.0 it is not possible to have a mapped property of a type that the database provider doesn't support. The workaround is to not map the property and map the field only.

@ardalis
Copy link
Author

ardalis commented Feb 16, 2018

Thanks, I'll have a look at type conversion.

@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