Ядро Entity Framework: модель заполнена неправильно

#c# #asp.net-core #entity-framework-core

#c# #asp.net-ядро #.net-ядро #entity-framework-core

Вопрос:

Я новичок в EF Core, и, используя это руководство, я получил неожиданный результат, а именно: данные выглядят нормально в модели, когда они только что заполнены, но в новом экземпляре контекста свойство навигации в основном объекте не заполнено, а свойство навигации в зависимомсущность заполняется особым образом.

Вот модель (код из руководства немного изменен):

 using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace EFGetStarted
{
    public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            options.UseSqlite("Data Source=blogging.db");
        }
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }
        public List<Post> Posts { get; } = new List<Post>();

        public override string ToString()
        {
            return
                "{"  
                "BlogId: "   BlogId   ", "  
                "Name: "   Name   ", "  
                "Posts.Count: "   Posts.Count  
                "}";
        }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Content { get; set; }
        public int BlogId { get; set; }
        public Blog Blog { get; set; }

        public override string ToString()
        {
            return
                "{"   
                "PostId: "   PostId   ", "  
                "Content: "   Content   ", "  
                "BlogId: "   BlogId   
                "}";
        }
    }
}
 

И вот программа:

 using static System.Console;

namespace EFGetStarted
{
    class Program
    {
        static void Main()
        {
            CreateContents(new BloggingContext());
            Print(new BloggingContext());
        }

        static void CreateContents(BloggingContext db)
        {
            AddBlog(db);
            Print(db);
        }

        private static void AddBlog(BloggingContext db)
        {
            var blog = new Blog { Name = "Blog 1" };
            db.Add(blog);

            blog.Posts.Add(new Post { Content = "Post 1" });
            blog.Posts.Add(new Post { Content = "Post 2" });
            blog.Posts.Add(new Post { Content = "Post 3" });

            db.SaveChanges();
        }

        private static void Print(BloggingContext db)
        {
            PrintBlogs(db);
            PrintPosts(db);
        }

        private static void PrintBlogs(BloggingContext db)
        {
            WriteLine("---- Blogs: ----");
            foreach (var blog in db.Blogs) Print(blog);
            WriteLine("");
        }

        private static void PrintPosts(BloggingContext db)
        {
            WriteLine("---- Posts: ----");
            foreach (var post in db.Posts) Print(post);
            WriteLine("");
        }

        private static void Print(Blog blog)
        {
            WriteLine(blog);
            foreach (var post in blog.Posts) WriteLine(post);
            WriteLine();
        }

        private static void Print(Post post)
        {
            WriteLine(post);
            WriteLine(post.Blog);
            foreach (var blogPost in post.Blog.Posts) WriteLine(blogPost);
            WriteLine();
        }
    }
}
 

Программа выдает следующий вывод:

 ---- Blogs: ----
{BlogId: 1, Name: 'Blog 1', Posts.Count: 3}
{PostId: 1, Content: 'Post 1', BlogId: 1}
{PostId: 2, Content: 'Post 2', BlogId: 1}
{PostId: 3, Content: 'Post 3', BlogId: 1}


---- Posts: ----
{PostId: 1, Content: 'Post 1', BlogId: 1}
{BlogId: 1, Name: 'Blog 1', Posts.Count: 3}
{PostId: 1, Content: 'Post 1', BlogId: 1}
{PostId: 2, Content: 'Post 2', BlogId: 1}
{PostId: 3, Content: 'Post 3', BlogId: 1}

{PostId: 2, Content: 'Post 2', BlogId: 1}
{BlogId: 1, Name: 'Blog 1', Posts.Count: 3}
{PostId: 1, Content: 'Post 1', BlogId: 1}
{PostId: 2, Content: 'Post 2', BlogId: 1}
{PostId: 3, Content: 'Post 3', BlogId: 1}

{PostId: 3, Content: 'Post 3', BlogId: 1}
{BlogId: 1, Name: 'Blog 1', Posts.Count: 3}
{PostId: 1, Content: 'Post 1', BlogId: 1}
{PostId: 2, Content: 'Post 2', BlogId: 1}
{PostId: 3, Content: 'Post 3', BlogId: 1}


---- Blogs: ----
{BlogId: 1, Name: 'Blog 1', Posts.Count: 0}


---- Posts: ----
{PostId: 1, Content: 'Post 1', BlogId: 1}
{BlogId: 1, Name: 'Blog 1', Posts.Count: 1}
{PostId: 1, Content: 'Post 1', BlogId: 1}

{PostId: 2, Content: 'Post 2', BlogId: 1}
{BlogId: 1, Name: 'Blog 1', Posts.Count: 2}
{PostId: 1, Content: 'Post 1', BlogId: 1}
{PostId: 2, Content: 'Post 2', BlogId: 1}

{PostId: 3, Content: 'Post 3', BlogId: 1}
{BlogId: 1, Name: 'Blog 1', Posts.Count: 3}
{PostId: 1, Content: 'Post 1', BlogId: 1}
{PostId: 2, Content: 'Post 2', BlogId: 1}
{PostId: 3, Content: 'Post 3', BlogId: 1}
 

Как вы можете видеть, при создании модели в блоге есть три сообщения, и каждое сообщение ссылается на блог с теми же тремя сообщениями.

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

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

Комментарии:

1. Что вы имеете в When printed in a new connection виду?

2. Возможно, это было неверно, но под «новым подключением» я подразумевал «новый экземпляр контекста». Я предположил, что при создании экземпляра контекста создается новое соединение с базой данных.

Ответ №1:

В коде есть ошибка. В коде нет инструкции include, поэтому вы не можете видеть сообщения. Попробуйте это:

 private static void PrintBlogs(BloggingContext db)
        {
          
            var blogs = db.Blogs.Include(i => i.Posts).ToArray();

            WriteLine("---- Blogs: ----");
            foreach (var blog in blogs) Print(blog);
            WriteLine("");
        }

        private static void PrintPosts(BloggingContext db)
        {
            var posts = db.Posts.Include(i => i.Blog).ToArray();

            WriteLine("---- Posts: ----");
            foreach (var post in posts) Print(post);
            WriteLine("");
        }
 

Теперь результат:

 
---- Blogs: ----
{BlogId: 1, Name: Blog 1, Posts.Count: 3}
{PostId: 1, Content: Post 1, BlogId: 1}
{PostId: 2, Content: Post 2, BlogId: 1}
{PostId: 3, Content: Post 3, BlogId: 1}


---- Posts: ----
{PostId: 1, Content: Post 1, BlogId: 1}
{BlogId: 1, Name: Blog 1, Posts.Count: 3}
{PostId: 1, Content: Post 1, BlogId: 1}
{PostId: 2, Content: Post 2, BlogId: 1}
{PostId: 3, Content: Post 3, BlogId: 1}

{PostId: 2, Content: Post 2, BlogId: 1}
{BlogId: 1, Name: Blog 1, Posts.Count: 3}
{PostId: 1, Content: Post 1, BlogId: 1}
{PostId: 2, Content: Post 2, BlogId: 1}
{PostId: 3, Content: Post 3, BlogId: 1}

{PostId: 3, Content: Post 3, BlogId: 1}
{BlogId: 1, Name: Blog 1, Posts.Count: 3}
{PostId: 1, Content: Post 1, BlogId: 1}
{PostId: 2, Content: Post 2, BlogId: 1}
{PostId: 3, Content: Post 3, BlogId: 1}

 

Комментарии:

1. Сергей, спасибо за ваш ответ! Я действительно ценю это! Я не пытался упростить этот код, так было в руководстве, я просто удалил пару полей, которые имели какое-либо значение. Я попробовал ваше решение, но получил некоторые ошибки компиляции, связанные с атрибутами InverseProperty. Я исправил ошибки, вот код, для которого я смог скомпилировать и сгенерировать миграцию: «

2. Извините, это была опечатка в моем ответе. Я не винил вас, чтобы упростить, я собирался упомянуть Microsoft. И я исправил еще одну опечатку. Вместо «Post.Blog» я набрал «Blog.Post». Теперь это исправлено. Вы можете проверить еще раз. Все должно работать правильно.

3. Вот код, для которого я смог скомпилировать и сгенерировать миграцию: « public partial class Blog { … [InverseProperty(«Blog»)] общедоступная виртуальная ICollection<Post> Сообщения { получить; установить; } … } открытый класс Post { … [InverseProperty(«Posts»)] общедоступный виртуальный блог Blog { get; set; } … } « Но, как я уже упоминал, это работало точно так же.

4. В вашем сообщении класса я не вижу [ForeignKey(nameof(BlogId))] это очень важно. без этого он не будет работать должным образом.

5. Длина комментария ограничена, поэтому я опустил части, которые точно такие же, как в вашем коде, включая ту часть, которую вы упомянули выше.