2013-12-02 6 views
2

Моя модель имеет базовый класс, который НЕ является абстрактным и не имеет определенного ключа (это внешний класс, который я не могу изменить). Вместо этого я определил производный класс с свойством MyID. Что-то вроде этого:Как игнорировать не абстрактный базовый класс в Entity Framework?

public class MyBaseClass // From external assembly 
{ 
    //public int ID { get; set; } 
    public string Name { get; set; } 
} 

public class MyClass // From external assembly 
{ 
    public int ID { get; set; } 
    public List<MyBaseClass> Objects { get; set; } 
} 

public class MyDerivedClass : MyBaseClass 
{ 
    public Guid MyID { get; set; } 

    public MyDerivedClass() 
    { 
     MyID = Guid.NewGuid(); 
    } 
} 

public class MyClasses : DbContext 
{ 
    public DbSet<MyClass> Classes { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<MyDerivedClass>().HasKey(entity => entity.MyID); 
     modelBuilder.Entity<MyDerivedClass>().Map(entity => 
     { 
      entity.MapInheritedProperties(); 
      entity.ToTable("MyBaseClass"); 
     }); 
     modelBuilder.Ignore<MyBaseClass>(); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Database.SetInitializer<MyClasses>(new DropCreateDatabaseIfModelChanges<MyClasses>()); 
     var myClass = new MyClass() // Just as example, in real code is somethog like: MyClass myClass = ExtenalAssembly.getMyClass() 
     { 
      ID = 0, 
      Objects = new List<MyBaseClass>() 
      { 
       new MyBaseClass() 
       { 
        //ID = 0, 
        Name = "My Test Object 1" 
       }, 
       new MyBaseClass() 
       { 
        //ID = 1, 
        Name = "My Test Object 2" 
       } 
      } 
     }; 
     Mapper.CreateMap<MyBaseClass, MyDerivedClass>(); 
     for (var index = 0; index < myClass.Objects.Count; index++) 
     { 
      myClass.Objects[index] = Mapper.Map<MyDerivedClass>(myClass.Objects[index]); 
     } 
     var myObjects = new MyClasses(); 
     myObjects.Classes.Add(myClass); 
     myObjects.SaveChanges(); 
    } 
} 

Если я закомментируйте строку modelBuilder.Ignore<MyBaseClass>(); среда выполнения генерирует исключение, потому что MyBaseClass не имеет ключевое значение; с другой стороны, когда я включаю эту строку, чтобы не сохранять состояние экземпляра базового класса, а только состояние экземпляра производного класса, система не сохраняет никаких данных в сгенерированных таблицах.

Что мне делать, чтобы сохранить только состояние производного экземпляра?

+0

Вы должны переместить идентификатор базового класса или предоставить Id удостоверений в этом классе. – Fals

+1

@Fals Какая часть * это внешний класс, который я не могу изменить * вы пропустили? :) – MarcinJuraszek

ответ

1

Я запустил ваше решение с помощью EF6.0.1 от NuGet, и все это проверено (без ошибок). Я сохранил данные правильно, и оба поля были сохранены, как ожидалось.

Если я читаю вас правильно, это звучит так, как будто вы не хотите, чтобы свойство базового класса отображалось Name. Если это так, вы можете добавить эту строку в свой modelBuilder и восстановить свой файл миграции.

modelBuilder.Entity<MyDerivedClass>().Ignore(d => d.Name); 

Это дает мне "Up" сценарий

CreateTable(
    "dbo.MyBaseClass", 
     c => new 
      { 
       MyID = c.Guid(nullable: false), 
      }) 
     .PrimaryKey(t => t.MyID); 

После запуска ваш главный, только идентификатор сохраняется.

+0

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

0

Измените свойство навигационной коллекции, чтобы использовать производный тип в качестве параметра типового типа (в конце концов, вы указываете EF игнорировать MyDerivedType). Это заставит все работать.

Обновление: если вы не можете изменить MyClass класс, а затем получить новый класс из MyClass со свойством, которое преобразует MyClass.Objects в/из List<MyDerivedClass> и использовать этот недавно полученный класс вместо MyClass.

Update 2: сделал код более безопасным путем автоматического преобразования экземпляров MyBaseClass в MyClass.Objects экземпляры MyDerivedClass при доступе через MyClass2.DerivedObjects собственности. Это позволяет добавлять MyBaseClass экземпляры в список MyClass.Objects (как показано на вопросе), не опасаясь недействительных исключений литья при доступе с помощью MyClass2.DerivedObjects.

Update 3: еще раз обновлен, чтобы сделать MyClass2 обертку MyClass вместо получения от него

// DbSet property in your DbContext 
public DbSet<MyClass2> Classes { get; set; } 

// continue to ignore MyBaseClass 
... 
modelBuilder.Ignore<MyBaseClass>(); 
... 


public class MyBaseClass // From external assembly 
{ 
    //public int ID { get; set; } 
    public string Name { get; set; } 
} 

public class MyDerivedClass : MyBaseClass 
{ 
    public Guid MyID { get; set; } 

    public MyDerivedClass() 
    { 
     MyID = Guid.NewGuid(); 
    } 

    public MyDerivedClass(MyBaseClass myBaseClass) : this() 
    { 
     // map myBaseClass to `this` here: 
     this.Name = myBaseClass.Name; 
    } 
} 

public class MyClass 
{ 
    public int ID { get; set; } 
    public List<MyBaseClass> Objects { get; set; } 
} 

public class MyClass2 
{ 
    public static implicit operator MyClass(MyClass2 mc2) 
    { 
     return mc2._myClass; 
    } 

    public virtual List<MyDerivedClass> Objects 
    { 
     get 
     { 
      for(int i = 0; i < _myClass.Objects.Count; ++i) 
      { 
       var o = _myClass.Objects[ i ]; 

       if(null == (o as MyDerivedClass)) 
       { 
        _myClass.Objects[ i ] = new MyDerivedClass(o); 
       } 
      } 

      return _myClass.Objects.Cast<MyDerivedClass>().ToList(); 
     } 
     set 
     { 
      _myClass.Objects = value.Cast<MyBaseClass>().ToList(); 
     } 
    } 

    public int ID 
    { 
     get { return _myClass.ID; } 
     set { _myClass.ID = value; } 
    } 

    private MyClass _myClass; 

    public MyClass2() 
    { 
     _myClass = new MyClass(); 
    } 

    public MyClass2(MyClass myClass) 
    { 
     if(null == myClass) 
      throw new ArgumentNullException("myClass"); 

     _myClass = myClass; 
    } 
} 

Код приложения:

using(var db = new TestContext()) 
{ 
    var myClass = new MyClass() 
    { 
     ID = 0, 
     Objects = new List<MyBaseClass>() 
     { 
      new MyBaseClass() 
      { 
       Name = "Object 1" 
      }, 
      new MyBaseClass() 
      { 
       Name = "Object 2" 
      } 
     } 
    }; 

    var myClass2 = new MyClass2(myClass); 

    db.Classes.Add(myClass2); 

    db.SaveChanges(); 
} 
+0

Это был только пример, показывающий, что происходит, но у меня нет контроля над тем, как создается экземпляр «myClass», потому что внешняя сторонняя библиотека инициализирует и возвращает экземпляр таким образом (используя MyBaseClass). –

+0

alrighty then - вывести новый класс из 'MyClass', который бросает' base.Objects' в/из 'List ' и использует этот новый класс (который я назвал 'MyClass2' в моем примере) вместо' MyClass' – Moho

+0

Извините, я поместил инициализацию экземпляра myClass как пример, но я не могу изменить способ его создания. В моем реальном коде есть что-то вроде MyClass myClass = ExtenalAssembly.getMyClass(). Этот вызов возвращает мне экземпляр, уже инициализированный значениями типов «MyClass», содержащими коллекцию «MyBaseClass». –

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