C#
C#

help

Root Question Message

Avalari
Avalari11/4/2022
[Entity Framework] Issues with .Include()

Hi all! I have some issues trying to get data from my SQL Db.

Having 3 models (Set, SetContent and Item):
public class Set
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Code { get; set; }
        public DateTime? ReleaseDate { get; set; }
        public string Type { get; set; }
        public string? Series { get; set; }
        public string? Description { get; set; }

        public ICollection<SetContent> Content { get; set; }
    }

--------------------------

public class SetContent
    {
        public int Id { get; set; }
        public int SetId { get; set; }
        public string? SetCode { get; set; }
        public string? SetRarity { get; set; }
        public string? SetRarityCode { get; set; }
        public int ItemId { get; set; }

        public virtual Item Item { get; set; }
    }

--------------------------

public class Item
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Type { get; set; }
        public string Desc { get; set; }
        public string ImageBig { get; set; }
        public string ImageSmall { get; set; }

        public List<SetContent> Sets { get; set; } = new List<SetContent>();
    }


I have 3 pages:
- one to display all Sets
- one to display Content of specific Set
- one to display the specific Item details

The issue that I have is with the third one.
For the Item view page I would like to have a segment showing in what <Set> you can find it.

So my question is how should I form the call in order to get something like Item with SetContent with Set (name and releaseDate):
{
  "id": 0,
  "name": "string",
  "type": "string",
  "desc": "string",
  "imageBig": "string",
  "imageSmall": "string",
  "sets": [
    {
      "id": 0,
      "setId": 0,
      "setCode": "string",
      "setRarity": "string",
      "setRarityCode": "string",

      "Name": "string",
      "ReleaseDate": "DateTime"
    }
  ]
}
Angius
Angius11/4/2022
Ewww, virtual
Angius
Angius11/4/2022
Technically, fetching an item from Items and using .Include(i => i.Sets) would just work
Angius
Angius11/4/2022
But I'd recommend opting for .Select() instead
Message Not Public

Sign In and Join Server To See

11/4/2022
Avalari
Avalari11/5/2022
anything bad with using virtual? 😄
Avalari
Avalari11/5/2022
forgot to mentioned that I've tried it since the beginning and even with:
builder.Services.AddControllersWithViews()
                .AddJsonOptions(x => x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);

it thrown me into a loop of Item -> Sets -> Item -> Sets=null
Angius
Angius11/5/2022
It implies you're using lazy loading, which is generally not a great idea
Angius
Angius11/5/2022
Selecting instead of including should help
Message Not Public

Sign In and Join Server To See

11/5/2022
Message Not Public

Sign In and Join Server To See

11/5/2022
Angius
Angius11/5/2022
Not anonymous, yes DTOs
Angius
Angius11/5/2022
And in most cases you'll get better performance out of fetching, say, a blogpost and its associated tags in one go, rather then querying the database for the blogpost, and then querying it again for each tag
D.Mentia
D.Mentia11/5/2022
This would be because of lazy loading 😛
When you explicitly load, you don't have those problems

In this case your SetContent should contain a Set (as well as the SetId), which doesn't do anything in the db, just makes it easier to work with on the code side

Then you can do Context.Items.Include(i => i.Sets).ThenInclude(s => s.Set)
Avalari
Avalari11/5/2022
I went with @85903769203642368's rout and just formed a nested Select
Avalari
Avalari11/5/2022
I get the intended return without obsolete data
D.Mentia
D.Mentia11/5/2022
Yep that works too
Avalari
Avalari11/5/2022
also tried:
var item = await _context.Items
                .Where(s => s.Id == id)
                .FirstOrDefaultAsync<Item>();

            _context.Entry(item).Collection(s => s.Sets)
                .Query()
                .Include(b => b.Set)
                .Load();
Angius
Angius11/5/2022
That only queries the database again for little to no reason
Angius
Angius11/5/2022
So, yeah, good you went with .Select()
Avalari
Avalari11/5/2022
@85903769203642368 thanks for helping out (y)
Avalari
Avalari11/5/2022
tho should I get rid of Virtual tags in case of Selecting what I want?
Angius
Angius11/5/2022
ye
Message Not Public

Sign In and Join Server To See

11/5/2022
Angius
Angius11/5/2022
.Include() is eager, yes, but virtual allows for lazy loading to be used
Angius
Angius11/5/2022
If you're using it, don't
Angius
Angius11/5/2022
If you aren't, why have virtual there
Angius
Angius11/5/2022
One way or another, it's unnecessary
ContactFrequently Asked QuestionsJoin The DiscordBugs & Feature RequestsTerms & Privacy