1

Given the following three classes:

public class Make
{
    public int MakeId { get; set; }
    [Required]
    [StringLength(255)]
    public string Name { get; set; }
    public ICollection<Model> Models { get; set; }

    public Make()
    {
        Models = new Collection<Model>();
        Name = string.Empty;
    }
}
public class Model
{
    public int ModelId { get; set; }
    [Required]
    [StringLength(255)]
    public string Name { get; set; }
    public Make Make { get; set; }
    public int MakeId { get; set; }

    Model()
    {
        Name = string.Empty;
        Make = new Make();
    }
}
public class VegaDbContext : DbContext
{
    public VegaDbContext(DbContextOptions<VegaDbContext> options) : base(options)
    {

    }
    public DbSet<Make> Makes { get; set; }
    public DbSet<Model> Models { get; set; }
}

I want to make an API endpoint available that returns Make and related Models. The function for that looks like the following:

[HttpGet("/api/makes")]
public async Task<IEnumerable<MakeResource>> GetMakes()
{
    // Here Models is not being loaded!
    var makes = await dbContext.Makes.Include(m => m.Models).ToListAsync();

    // This is manually loading the required data, which works. But I don't want to do this.
    foreach (var make in makes)
    {
        make.Models = await dbContext.Models.Where(m => m.MakeId == make.MakeId).ToListAsync();
    }

    // This mapper part is not of interest
    return mapper.Map<IList<Make>, IList<MakeResource>>(makes);
}

The problem here is, that the line fetching the Makes does not include the Models, instead I get an empty list. According to Microsoft's documentation, this is a "fully defined relationship" and the models with the correct foreign keys should be loaded.

It seems there have been some changes with the introduction of EFC 3.0 and it seems to have worked before. I have no idea why this is the case. If anyone has an idea, I'd be glad! The database was created using code first migrations. The versions are:

.NET Core SDK (reflecting any global.json):
 Version:   3.1.103
 Commit:    6f74c4a1dd

Runtime Environment:
 OS Name:     manjaro
 OS Version:  
 OS Platform: Linux
 RID:         arch-x64
 Base Path:   /usr/share/dotnet/sdk/3.1.103/

Host (useful for support):
  Version: 3.1.3
  Commit:  ed88943d24

.NET Core SDKs installed:
  3.1.103 [/usr/share/dotnet/sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.App 3.1.3 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.3 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

To install additional .NET Core runtimes or SDKs:
  https://aka.ms/dotnet-download

The database is filled with:

Makes Models

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
Muqq
  • 101
  • 1
  • 8
  • 1
    Where did you configure the PK-FK relationship for the entities ? Like in the `OnModelCreating()` callback – zafar May 05 '20 at 14:09
  • As I am using the default convention, EF is detecting that automatically when creating the migration. Would be weird if it detects that for creating the migration, but then not for querying, or am I mistaken here? – Muqq May 05 '20 at 14:11
  • Also i have two questions about `Model` class: 1) why it's constructor is private(typo?) and why do you have line `Make = new Make();` in it? – Guru Stron May 05 '20 at 14:11
  • @Muqq you are correct, you don't need to configure explicit relationship if you follow the convention. Make the constructor public, that should resolve the issue. Also creating `new Make()` object would be redundant. EF takes care of initializing one for you – zafar May 05 '20 at 14:14
  • @Muqq what database do you use? – Guru Stron May 05 '20 at 14:15
  • @GuruStron 1) Is indeed a typo, I fixed it, but didn't change the behaviour. 2) I want to use C# 8.0 feature for only explicit null values ```enabled```, therefore I need to initialise it. I tested both enabling and disabling to make sure it is not caused by this. 3) I will look into the configuration you linked, but for me that seems it's for FluentAPI which I don't want to use here. 4). SQL Server running inside a docker container. – Muqq May 05 '20 at 14:16
  • @Muqq you may configure your `VegaDbContext` to use the Logger factory and check the log to see if the the EF queries are translating to appropriate sql query. If it is joining the `Makes` and `Models` in sql query, then something is wrong in the C# code – zafar May 05 '20 at 14:23
  • I will do that and post the findings here. Cannot do it directly though, so needs come time before I can report on that. – Muqq May 05 '20 at 14:29
  • 1
    here is the link to configure the console logger https://stackoverflow.com/a/53693858/3579354 – zafar May 05 '20 at 14:30
  • 1
    JFYI @Muqq, I was able to reproduce the issue for inmemory provider, commenting out `Make = new Make()` helped =))) – Guru Stron May 05 '20 at 14:33
  • @GuruStron Indeed, that was the problem! Thank you very much! Don't really understand why though, also if combining that with this new C# 8.0 feature for only explicit nullable types, I can imagine that this will introduce even weirder issues across a larger code base, so maybe I should refrain from it for now until it's more mature. One more question though: How did the in memory provider help you figuring this out? – Muqq May 05 '20 at 14:38
  • Then will add this as an answer =) – Guru Stron May 05 '20 at 14:39
  • @Muqq as for the nullable types I think you can disable it for your database models, keeping this feature for the rest of the code. – Guru Stron May 05 '20 at 14:43

1 Answers1

1

I was able to reproduce the issue for EF Core In-Memory Database Provider, commenting out line Make = new Make(); in the Model constructor resolved the issue.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132