Search code examples
c#asp.net-coreef-code-firstasp.net-core-webapief-database-first

EF Codefirst GET Method Sub-models not coming


EF Codefirst GET Method Sub-models not coming. With Database First I want the following Json result. However, I cannot get the following Json with Code First. I need a structure to work on the model relationship without running the include method.

Model.cs

        public class BloggingContext : DbContext
        {
            public BloggingContext(DbContextOptions<BloggingContext> options) : base(options){ }

            public virtual DbSet<Blog> Blogs { get; set; }
            public virtual DbSet<Post> Posts { get; set; }
        }

        public class Blog
        {
            public int BlogId { get; set; }
            public string Url { get; set; }

            public ICollection<Post> Posts { get; set; }
        }

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

            public int BlogId { get; set; }
            public Blog Blog { get; set; }
        }

ValuesController.cs

        private readonly BloggingContext db;

        public ValuesController(BloggingContext db)
        {
            this.db = db;
        }

        // GET api/values
        [HttpGet]
        public ActionResult<IEnumerable<Blog>> Get()
        {
            return db.Blogs.ToList();
        }

Startup.cs

var connection = @"Server=(localdb)\mssqllocaldb;Database=EFGetStarted.AspNetCore.NewDb;Trusted_Connection=True;ConnectRetryCount=0";
            services.AddDbContext<Models.Model.BloggingContext>
                (options => options.UseSqlServer(connection));

Database Tables

Blogs Table Result
__________________
BlogId  |   Url
1       |   asdasd1
2       |   asdas2

Posts Table Result
__________
PostId  |   Title   |   Content |   BlogId
1       |   asdasd  |   fdg     |   1
2       |   fsg     |   asda    |   1
3       |   dsgfsdg |   sgf     |   2

Run Result

[
  {
    "blogId": 1,
    "url": "asdasd1",
    "posts": null
  },
  {
    "blogId": 2,
    "url": "asdas2",
    "posts": null
  },
  {
    "blogId": 3,
    "url": "asdsad3",
    "posts": null
  }
]

I want to work with the above codes

[
  {
    "blogId": 1,
    "url": "asdasd1",
    "posts": [
      {
        "PostId": 1,
        "Title": "asdasd",
        "Content": "fdg"
      },
      {
        "PostId": 2,
        "Title": "fsg",
        "Content": "asda"
      }
    ]
  },
  {
    "blogId": 2,
    "url": "asdas2",
    "posts": {
      "PostId": 2,
      "Title": "fsg",
      "Content": "asda"
    }
  },
  {
    "blogId": 3,
    "url": "asdsad3",
    "posts": null
  }
]

Solution

  • EF does not load relationships automatically by default, as that would require SQL JOINs, which wouldn't be desired in most scenarios - only when you actually wanted the related data.

    As a result, you have two options: eagerly load the relationships using Include, or rely on lazy loading to query the related data, just in time, when accessed. Lazy loading is not enabled by default, because it's frankly a bit of an anti-pattern, and you shouldn't be using it unless you're well aware of the effects and possible issues.

    In this particular scenario, those possible issues would rear their ugly heads quickly and dramatically. Since you're serializing the entity, the serializer would touch every relationship, causing a query to be individually issued to get that one particular set of related data. This then would occur also for every blog you're iterating through. And, if any of the related items, had related things themselves, you'd be issuing an exponential amount of additional queries. In short, lazy-loading something like this, could result in 100s if not thousands or more queries being issued all at once - not something you want happening to say the least.

    In short, you need to eagerly load the relationships you care about, regardless of whether that's your preferred approach or not. There's no free lunch here. It's either one query with all the joins all at once or tons of queries for each piece of data individually later.

    Alternatively, you might consider using something like OData or GraphQL. That alone doesn't solve the issue: by default, related items won't be included. However, both of those provide the ability to request related data as part of the request, allowing the client to retrieve just the relationships they need or are interested in. That solves your issue of not wanting to specify and query every single relationship all at once, while still giving the client an easy way to get it should they need/want it.