class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public DateTime Birthday { get; set; }
        public List<Book> Books { get; } = new List<Book>();

        public override string ToString()
        {
            return $"Id:{Id}, Name:{Name}, Age:{Age}, Birthday:{Birthday}";
        }
    }
    class Book
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Author { get; set; }
        public int Age { get; set; }
        public Person Owner { get; set; }

        public override string ToString()
        {
            return $"name:{Name}, author:{Author}, owner:{Owner.Name}";
        }
    }

    class BookConfig : IEntityTypeConfiguration<Book>
    {
        public void Configure(EntityTypeBuilder<Book> builder)
        {
            builder.ToTable("Book");
            //builder.ToView("book_view");
            // nvarchar is changed to varchar, but length is decided by HasMaxLength(50).
            builder.Property(e => e.Name).HasColumnType("nvarchar(80)").HasMaxLength(50).IsRequired();
            builder.Property(e => e.Author).HasColumnName("author_name").HasMaxLength(100).IsRequired();

            builder.Ignore(e => e.Age);

            // add one-to-many mapping
            builder.HasOne<Person>(b => b.Owner).WithMany(p => p.Books).IsRequired();

        }
    }

    // 也可以在PersonConfig中配置
    class PersonConfig : IEntityTypeConfiguration<Person>
    {
        public void Configure(EntityTypeBuilder<Person> builder)
        {
            builder.HasMany<Book>(p => p.Books).WithOne(b => b.OwnerId).IsRequired();
        }
    }
    class Program
    {
        static async Task Main(string[] args)
        {
            using (MyDbContext ctx = new MyDbContext())
            {
                await ReadBook(ctx);
            }
        }

        private static Task ReadBook(MyDbContext ctx)
        {
            // use include() to include reference properties.
            Book book = ctx.Books.Include(b => b.Owner).FirstOrDefault(b => b.Id == 3);

            Console.WriteLine(book.Id);
            Console.WriteLine(book.Name);
            Console.WriteLine(book.Author);
            // Owner is null if above include() is not called.
            Console.WriteLine(book.Owner.Name);

            return Task.CompletedTask;
        }

        private static Task ReadPersons(MyDbContext ctx)
        {
            // use include() to include reference properties.
            Person person = ctx.Persons.Include(p => p.Books).FirstOrDefault(p => p.Name == "h1");

            // Books is null if above include() is not called.
            foreach (Book b in person.Books)
            {
                Console.WriteLine(b);
            }

            return Task.CompletedTask;
        }

        private static async Task SavePersons(MyDbContext ctx)
        {
            var books = new Book[]
                {
                    new Book()
                    {
                        Name = "book1",
                        Author = "author01",
                    },
                    new Book()
                    {
                        Name = "book2",
                        Author = "author02",
                    },
                };

            Person p1 = new Person()
            {
                Name = "h1",
            };
            p1.Books.AddRange(books);

            ctx.Persons.Add(p1);
            // no need to add books explicitly.
            await ctx.SaveChangesAsync();
        }
    }