Native Entity frameworks express exception when using Extension Method for Free API

I am using Entity Framework 6, Code-First with Fluent-API.

I have the following extension method to set a primary key on my objects:

public static class ConfigurationExtensions
{
    public static void HasPrimaryKey<TEntityType>(this EntityTypeConfiguration<TEntityType> configuration)
        where TEntityType : class, IEntity
    {
        configuration
            .HasKey(m => m.Id)
            .Property(t => t.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}

      

with each Code-First Entity implementing the following simple interface:

public interface IEntity
{
    int Id { get; }
}

      

Now, suppose I have the following Entity:

public class MyEntity : IEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
}

      

which I am setting up using the Fluent-API in my DbContext

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new MyEntityConfiguration());
}

      

with a config class for an object:

public class MyEntityConfiguration : EntityTypeConfiguration<MyEntity>
{
    public MyEntityConfiguration()
    {
        this.HasPrimaryKey()
    }
}

      

Oddly enough, when I execute Add-Migration

in the Package Manager Console, I get the following exception:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: The property 'Id' cannot be used as a key property on the entity 'MyEntity' because the property type is not a valid key type. Only scalar types, string and byte[] are supported key types.
   at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.Key(PropertyInfo propertyInfo, Nullable`1 overridableConfigurationParts)
   at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.Key(IEnumerable`1 keyProperties)
   at System.Data.Entity.ModelConfiguration.EntityTypeConfiguration`1.HasKey[TKey](Expression`1 keyExpression)
   at ConfigurationExtensions.HasPrimaryKey[TEntityType](EntityTypeConfiguration`1 configuration) in C:\path\ConfigurationExtensions.cs

      

This is weird because when I refactor the code in the extension method back to the constructor, for example:

public class MyEntityConfiguration : EntityTypeConfiguration<MyEntity>
{
    public MyEntityConfiguration()
    {
        HasKey(m => m.Id)
        .Property(t => t.Id)
        .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}

      

then the exception is not thrown by the framework and everything works as expected. What for? Framework error?

+3


source to share


1 answer


The error message is misleading. The actual problem is pretty trivial. EF only supports properties that have a property setter.

Since the expression m => m.Id

in your generic extension method binds to an Id

interface property IEntity

that does not have a setter (unlike an implementation class), EF does not consider it a valid property and throws an exception with a misleading property type message.

To fix this, just define a setter for the property Id

inside the interface:



public interface IEntity
{
    int Id { get; set; }
}

      

It can also be resolved by creating the lambda expression manually using class methods Expression

, but I think it is easier to change the interface :) But here's the completeness and in case you don't want to break your interface design:

public static class ConfigurationExtensions
{
    public static void HasPrimaryKey<TEntityType>(this EntityTypeConfiguration<TEntityType> configuration)
        where TEntityType : class, IEntity
    {
        var parameter = Expression.Parameter(typeof(TEntityType), "m");
        var keyProperty = Expression.Lambda<Func<TEntityType, int>>(Expression.Property(parameter, nameof(IEntity.Id)), parameter);
        configuration
            .HasKey(keyProperty)
            .Property(keyProperty)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}

      

+2


source







All Articles