财产隐藏时播种失败

时间:2015-06-12 15:42:19

标签: c# entity-framework generics properties entity-framework-6

这比任何事都更像是一种思考。请考虑以下类:

public abstract class Entity : IEntity
{
    public object Id { get; set; }

    private string name;
    [Required]
    public string Name
    {
        get { return name ?? Guid.NewGuid().ToString(); }
        set { name = value; }
    }

    private DateTime? createdDate;
    [DataType(DataType.DateTime)]
    public DateTime CreatedDate
    {
        get { return createdDate ?? DateTime.UtcNow; }
        set { createdDate = value; }
    }

    [DataType(DataType.DateTime)]
    public DateTime? ModifiedDate { get; set; }

    public string CreatedBy { get; set; }

    public string ModifiedBy { get; set; }

    [Timestamp]
    public Byte[] Version { get; set; }
}

public abstract class Entity<T> : Entity, IEntity<T>
{
    [Key]
    new public T Id
    {
        get { return base.Id != null ? (T)base.Id : default(T); }
        set { base.Id = value; }
    }
}

Entity<T>隐藏Id上的Entity属性,将其替换为正确的类型。如果我从这里创建一个实体:

public class Foo : Entity<int>
{
    ...
}

并将其添加到上下文中:

public virtual IDbSet<Foo> Foos { get; set; }

实体框架愉快地创建了适当的表。所有核心EF函数都可以正常工作:我可以查询,保存,更新,删除等等。

但是,如果我尝试播种:

context.Foos.AddOrUpdate(
    r => r.Name,
    new Foo { ... },
    new Foo { ... },
    ...
);

并运行update-database。第一次,播种工作正常。然而,在每次后续运行中,播种都会引发异常:

Running Seed method.
System.Reflection.AmbiguousMatchException: Ambiguous match found.
   at System.Data.Entity.Utilities.TypeExtensions.GetAnyProperty(Type type, String name)
   at System.Data.Entity.Migrations.DbSetMigrationsExtensions.<>c__DisplayClass9`1.<GetKeyProperties>b__8(EdmMember km)
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Data.Entity.Migrations.DbSetMigrationsExtensions.AddOrUpdate[TEntity](DbSet`1 set, IEnumerable`1 identifyingProperties, InternalSet`1 internalSet, TEntity[] entities)
   at System.Data.Entity.Migrations.DbSetMigrationsExtensions.AddOrUpdate[TEntity](IDbSet`1 set, Expression`1 identifierExpression, TEntity[] entities)
   ...
   at System.Data.Entity.Migrations.DbMigrationsConfiguration`1.OnSeed(DbContext context)
   at System.Data.Entity.Migrations.DbMigrator.SeedDatabase()
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.SeedDatabase()
   at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
   at System.Data.Entity.Migrations.DbMigrator.UpdateInternal(String targetMigration)
   at System.Data.Entity.Migrations.DbMigrator.<>c__DisplayClassc.<Update>b__b()
   at System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
   at System.Data.Entity.Migrations.Infrastructure.MigratorBase.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
   at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
   at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration)
   at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run()
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Update(String targetMigration, Boolean force)
   at System.Data.Entity.Migrations.UpdateDatabaseCommand.<>c__DisplayClass2.<.ctor>b__0()
   at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
Ambiguous match found.

现在,我理解为什么这种情况正在发生。 EF正在使用反射并获得Id的两个结果,一个来自Entity,另一个来自Entity<T>。它无法确定使用哪个,并且失败。我的问题是:我能做些什么来帮助消除歧义吗?

对于它的价值,我正在运行EF 6.1.3。

1 个答案:

答案 0 :(得分:0)

你可以......避免在涉及一个关键属性时隐藏属性。 :)

在任何情况下,如果你查看他们的源代码,当AddOrUpdate找到使用给定属性的现有实体(例如r => r.Name)时,它首先会替换关键属性,然后再使用{{1更新非键属性。问题是它通过获取属性名称和过滤 - Entry(existing).CurrentValues.SetValues(entity)列表来获取关键属性 - GetRuntimeProperties()给出的列表,其中包含继承的属性。< / p>

也许另一种解决方案是让EF团队改变它?看起来像是一个边缘案例。