0

Это скорее мыслящее упражнение, чем что-либо. Рассмотрим следующие классы:Просачивание не работает, когда свойство скрывается

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

Entity Framework счастливо создает соответствующую таблицу. Все основные функции 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.

+0

никакой другой конфигурации? – jjj

+0

Нет. Это все, что нужно. –

+0

Создана ли таблица, состоящая из двух столбцов Id? – jjj

ответ

0

Вы можете ... не скрывать свойство, когда оно связано с ключевым свойством. :)

В любом случае, если вы посмотрите на их исходный код, когда AddOrUpdate находит существующий объект, используя заданные свойства (например, r => r.Name), он первым заменяет ключевые свойства, перед использованием Entry(existing).CurrentValues.SetValues(entity) обновить без ключевых свойств , Проблема в том, что он получает ключевые свойства, получая список свойств имен и фильтрации - .Where(p => p.Name == name) - список, заданный GetRuntimeProperties(), который включает в себя унаследованные свойства.

Возможно, еще одно решение - заставить команду EF изменить это? Кажется, что это крошечный кейс.

+0

Немного о кромке, признаюсь. Причина, по которой я пытаюсь это сделать, заключается в том, что мне нужно иметь возможность работать с не общим интерфейсом и все еще ссылаться на свойство Id. –

Смежные вопросы