简体   繁体   中英

Entity framework migrations throws exception when using extension method for fluent API

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

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

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 every 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'm configuring with Fluent-API in my DbContext

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

with the configuration class for the entity:

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

Oddly 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

It is odd because when I refactor the code in the extension method back into the constructor, like this:

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. Why? Framework bug?

The exception message is misleading. The actual issue is quite trivial. EF supports only properties that have a property setter.

Since the expression m => m.Id in your generic extension method binds to the Id property of the IEntity interface, which has no setter (in contrast with the implementing class), EF does not consider it a valid property and throws the exception with the misleading message about property type.

To fix it, simply define setter for the Id property inside the interface:

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

It can also be resolved by building the lambda expression manually using the Expression class methods, but I think modifying the interface is easier :) But here it is for 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);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM