使用扩展方法进行流畅的API

时间:2017-06-06 14:27:22

标签: c# entity-framework

我正在使用Entity Framework 6,Code-First和Fluent-API。

我有以下扩展方法在我的实体上设置主键:

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);
    }
}

每个Code-First Entity实现以下简单接口:

public interface IEntity
{
    int Id { get; }
}

现在假设我有以下实体:

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

我在DbContext

中使用Fluent-API进行配置
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new MyEntityConfiguration());
}

使用实体的配置类:

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

奇怪的是,当我在程序包管理器控制台中执行Add-Migration时,我得到以下异常:

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

这很奇怪,因为当我将扩展方法中的代码重构为构造函数时,就像这样:

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

然后,框架不会抛出异常,一切都按预期工作。为什么?框架错误?

1 个答案:

答案 0 :(得分:2)

异常消息具有误导性。实际问题非常简单。 EF仅支持具有属性设置器的属性。

由于泛型扩展方法中的表达式m => m.Id绑定到Id接口的IEntity属性,该接口没有setter(与实现类相比),EF不会将其视为有效属性,并使用有关属性类型的误导性消息抛出异常。

要修复它,只需在界面中为Id属性定义setter:

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

也可以通过使用Expression类方法手动构建lambda表达式来解决它,但我认为修改界面更容易:)但这里是为了完整性,如果你不想打破你的界面设计:

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);
    }
}