Проблема с вашей реализацией заключается в том, что вы не описываете свою схему в Entity Framework, то есть не определяете отношения между вашими объектами.
Вот предложение, которое работает:
public class Foo
{
public int ID { get; set; }
public string Name { get; set; }
[ForeignKey("AFoo")]
public int? AFooID { get; set; }
public virtual AnotherFoo AFoo { get; set; }
public int? ParentID { get; set; }
public virtual Foo Parent { get; set; }
public virtual List<Foo> Children { get; set; } = new List<Foo>();
}
public class AnotherFoo
{
public int ID { get; set; }
public string Name { get; set; }
}
Обратите внимание на пару вещей:
- Я добавил свойство внешнего ключа
AFooID
к сущности AFoo.Entity Framework самостоятельно распознает связь между свойством AFoo и сущностью Foo, и при построении схемы он также добавляет внешний ключ к сущности AntoherFoo.Но этот ключ не будет доступен в вашем коде, потому что вы не добавили его в свойства класса Foo.Кроме того, лучше, чтобы вы имели полный контроль над созданием сценария и не позволяли многим воображению Entity Framework (что, тем не менее, неплохо). - A добавили аннотацию
[ForeignKey("AFoo")]
над AFooID
чтобы определить, что это внешний ключ для AFoo
параметра навигации.Обычно я должен также добавить аналогичную аннотацию над ID
, чтобы определить его как первичный ключ, но Entity Framework делает это автоматически для целочисленного свойства с именем ID
. - Я использовал ключевое слово
virtual
ввсе параметры навигации, чтобы позволить ленивую загрузку этих параметров.
Теперь, для вашего основного вопроса;Вы можете создать ссылку на потомков того же типа, как если бы это был любой другой тип.Но вы должны описать отношения!В этом примере я настраиваю отношение один ко многим, что означает, что у каждого foo может быть только один родитель, но много детей.Для этого я использовал Fluent API
:
public class MyContext: DbContext
{
public DbSet<Foo> Foos { get; set; }
public DbSet<AnotherFoo> AFoos { get; set; }
public MyContext()
: base("TestDB")
{
Configuration.LazyLoadingEnabled = true;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Foo>()
.HasOptional(f => f.Parent)
.WithMany(f => f.Children)
.HasForeignKey(f => f.ParentID)
.WillCascadeOnDelete(false);
}
}
Переопределите метод OnModelCreating
вашего класса DbContext
, чтобы более «плавно» указать схему вашей базы данных.
Теперь выполните следующие команды в консоли диспетчера пакетов, чтобы создать базу данных:
add-migration InitialDB
Он создаст файл cs, описывающий создаваемую схему.Проверьте, что вы хотите. update-database
Он создаст базу данных, используя строку подключения, определенную в вашем файле конфигурации, и схему из файла выше.
Запустите это демонстрационное приложение:
static void Main(string[] args)
{
var foo = new Foo { Name = "parent foo" };
var foo1 = new Foo { Name = "first foo child" };
var foo2 = new Foo { Name = "second foo child" };
foo.Children.Add(foo1);
foo.Children.Add(foo2);
using(var context = new MyContext())
{
context.Foos.Add(foo);
context.SaveChanges();
}
// another transaction to read the saved data
using(var context = new MyContext())
{
var readfoo = context.Foos.FirstOrDefault();
Console.WriteLine($"{readfoo.Name} has the following children:");
foreach(var child in readfoo.Children)
Console.WriteLine(child.Name);
}
Console.ReadKey();
}
Результаты ожидаемые:
Я надеюсь, что помог вам решить вашу проблему и немного лучше понять мир Entity Framework.