Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query :: Include with multiple navigations (including optional navigation) fails #5672

Closed
ImmePak opened this issue Jun 7, 2016 · 6 comments
Assignees
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Milestone

Comments

@ImmePak
Copy link

ImmePak commented Jun 7, 2016

I`am running ASP.NET Core 1.0 application, using EF7 rc2
Part of class structure:

public sealed class Member
{
    public Guid Id { get; set; }
    public MemberState State { get; set; }
}
public sealed class MemberState
{
     public Guid Id { get; set; }
     public Org Org { get; set; }
}

So, first goal was to make field Org in MemberState nullable and I configured EF context in the following way:

entity.Property<Guid?>("Idf_Org");
entity.HasOne(e => e.Org).WithMany().HasForeignKey("Idf_Org").IsRequired(false);

It was fine before i have need to search Members by Org.Id
First approach, that worked before i marked Org field as nullable:

_provider.Members.Include(m => m.State).ThenInclude(s => s.Org)
        .Where(m => m.State.Org.Id == idfOrg).ToList();

Now it start firing exception : Object reference not set to an instance of an object.
Considering the fact that database not contains any record of MemberState with NULL in Idf_Org it was quite strange.
but this one is working O_o but give me unappropriate result (all included instances is null)

_provider.Members.Where(m => m.State.Org.Id == idfOrg).ToList();

One way i found is to do direct SQL query in that way

_provider.Members.FromSql("SELECT mem.* "
         + "FROM [dbo].[Member] as mem "
         + "inner join [dbo].[State] as st on st.Id = mem.Idf_State "
         + "where st.Idf_Org = {0} ", idfOrg)
         .Include(m => m.State).ThenInclude(s => s.Org).ToList();

The question is follows:

  1. Is this behaviour is normal to EF ?
  2. Is there a way to get queries without using direct sql in this case ? In my app MemberState and included object Org are involved in many application parts as reports and so on and it is unappropriate to manage all queries with sql.
@divega
Copy link
Contributor

divega commented Jun 7, 2016

@maumar does this look familiar? In triage we discussed that the NRE may come from m.State.Org.Id if .Org is null, but we believe you improved the expansion to compensate for that. Was that after RC2?

@maumar
Copy link
Contributor

maumar commented Jun 7, 2016

@ImmePak can you provide some more information? im unable to reproduce this. Ideally provide standalone repro. Otherwise please provide code for Org class and a complete code for OnModelCreating. If you are using sample data, that would be great also.

@maumar
Copy link
Contributor

maumar commented Jun 7, 2016

@ImmePak as a workaround you can try using two queries, first one would gather all the Ids needed, and the other one would perform the query:

var memberIds = _provider.Members.Where(m => m.State.Org.Id == idfOrg).Select(m => m.Id).ToList();

var query = _provider.Members.Include(m => m.State).ThenInclude(s => s.Org)
        .Where(m => memberIds.Contains(m.Id)).ToList()

@ImmePak
Copy link
Author

ImmePak commented Jun 7, 2016

@maumar as you asked:

EF OnModelCreating code

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
     modelBuilder.Entity<OrgType>(entity =>
     {
           entity.ToTable("OrganizationType");
           entity.HasKey(e => e.Id);
     });
     modelBuilder.Entity<Org>(entity =>
     {
           entity.ToTable("Organization");
           entity.HasKey(e => e.Id);
           entity.Property<int>("Idf_TypeOrg");
           entity.HasOne(org => org.Type).WithMany().HasForeignKey("Idf_TypeOrg");
           entity.Property(x => x.ContactPerson).HasColumnName("Contacts");
     });

     modelBuilder.Entity<MemberState>(entity =>
     {
           entity.ToTable("State");
           entity.HasKey(e => e.Id);
           entity.Property<Guid?>("Idf_Org");
           entity.HasOne(e => e.Org).WithMany().HasForeignKey("Idf_Org").IsRequired(false);
           entity.Property<Guid>("Idf_District");
           entity.HasOne(e=>e.District).WithMany().HasForeignKey("Idf_District");
           entity.Property<int>("Idf_Class");
           entity.HasOne(e => e.Class).WithMany().HasForeignKey("Idf_Class");
           entity.Property<int>("Idf_Level");
           entity.HasOne(e => e.Level).WithMany().HasForeignKey("Idf_Level");
    });
    modelBuilder.Entity<Member>(entity =>
    {
           entity.HasKey(e => e.Id);
           entity.Property<Guid>("Idf_State");
           entity.HasOne(member => member.State).WithMany().HasForeignKey("Idf_State");
    });
....

Expanded class code

    public sealed class Org
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public OrgType Type { get; set; }
        public string UrAddress { get; set; }
        public string ContactPerson { get; set; }
        public string ExtraInfo { get; set; }
        public bool Active { get; set; }
   } 
   public sealed class MemberState
   {
        public Guid Id { get; set; }
        public Level Level { get; set; }
        public Org Org { get; set; }
        public Class Class { get; set; }
        public District District { get; set; }
   }
   public sealed class Member
   {
        public Guid Id { get; set; }
        public String Fio { get; set; }
        public String IdGto { get; set; }
        public Boolean Gender { get; set; }
        public DateTime Birthday { get; set; }
        public MemberState State { get; set; }
    }

@bricelam bricelam added this to the 1.0.1 milestone Jun 10, 2016
@maumar
Copy link
Contributor

maumar commented Jun 13, 2016

I was able to reproduce the issue. It is a legitimate bug, looking into it...

@rowanmiller rowanmiller added type-bug and removed pri0 labels Jul 1, 2016
@maumar maumar changed the title Nullable field issue Query :: Include with multiple navigations (including optional navigation) fails Jul 15, 2016
maumar added a commit that referenced this issue Jul 15, 2016
…optional navigation) fails

Problem was that our include logic for groupjoin was expecting outer and inner elements (on which the include was being performed) to be entities. However sometimes those elements would be TransparentIdentifiers (e.g. when the element comes from a result of SelectMany).

Fix is to also store (when necessary) the accessor from the outer/inner element to the entity that we want to include, extract the actual entity in runtime and apply include operations on that entity instead of the actual outer/inner element.
maumar added a commit that referenced this issue Jul 21, 2016
…optional navigation) fails

Problem was that our include logic for groupjoin was expecting outer and inner elements (on which the include was being performed) to be entities. However sometimes those elements would be TransparentIdentifiers (e.g. when the element comes from a result of SelectMany).

Fix is to also store (when necessary) the accessor from the outer/inner element to the entity that we want to include, extract the actual entity in runtime and apply include operations on that entity instead of the actual outer/inner element.
maumar added a commit that referenced this issue Jul 21, 2016
…optional navigation) fails

Problem was that our include logic for groupjoin was expecting outer and inner elements (on which the include was being performed) to be entities. However sometimes those elements would be TransparentIdentifiers (e.g. when the element comes from a result of SelectMany).

Fix is to also store (when necessary) the accessor from the outer/inner element to the entity that we want to include, extract the actual entity in runtime and apply include operations on that entity instead of the actual outer/inner element.
maumar added a commit that referenced this issue Jul 22, 2016
…optional navigation) fails

Problem was that our include logic for groupjoin was expecting outer and inner elements (on which the include was being performed) to be entities. However sometimes those elements would be TransparentIdentifiers (e.g. when the element comes from a result of SelectMany).

Fix is to also store (when necessary) the accessor from the outer/inner element to the entity that we want to include, extract the actual entity in runtime and apply include operations on that entity instead of the actual outer/inner element.
maumar added a commit that referenced this issue Jul 22, 2016
…optional navigation) fails

Problem was that our include logic for groupjoin was expecting outer and inner elements (on which the include was being performed) to be entities. However sometimes those elements would be TransparentIdentifiers (e.g. when the element comes from a result of SelectMany).

Fix is to also store (when necessary) the accessor from the outer/inner element to the entity that we want to include, extract the actual entity in runtime and apply include operations on that entity instead of the actual outer/inner element.

CR: Andrew
maumar added a commit that referenced this issue Jul 22, 2016
…optional navigation) fails

Problem was that our include logic for groupjoin was expecting outer and inner elements (on which the include was being performed) to be entities. However sometimes those elements would be TransparentIdentifiers (e.g. when the element comes from a result of SelectMany).

Fix is to also store (when necessary) the accessor from the outer/inner element to the entity that we want to include, extract the actual entity in runtime and apply include operations on that entity instead of the actual outer/inner element.

CR: Andrew
maumar added a commit that referenced this issue Jul 22, 2016
…optional navigation) fails

Problem was that our include logic for groupjoin was expecting outer and inner elements (on which the include was being performed) to be entities. However sometimes those elements would be TransparentIdentifiers (e.g. when the element comes from a result of SelectMany).

Fix is to also store (when necessary) the accessor from the outer/inner element to the entity that we want to include, extract the actual entity in runtime and apply include operations on that entity instead of the actual outer/inner element.

CR: Andrew
maumar added a commit that referenced this issue Jul 22, 2016
…optional navigation) fails

Problem was that our include logic for groupjoin was expecting outer and inner elements (on which the include was being performed) to be entities. However sometimes those elements would be TransparentIdentifiers (e.g. when the element comes from a result of SelectMany).

Fix is to also store (when necessary) the accessor from the outer/inner element to the entity that we want to include, extract the actual entity in runtime and apply include operations on that entity instead of the actual outer/inner element.

CR: Andrew
@maumar
Copy link
Contributor

maumar commented Jul 22, 2016

fixed in 5f48163

@maumar maumar closed this as completed Jul 22, 2016
@maumar maumar added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Jul 22, 2016
@ajcvickers ajcvickers modified the milestones: 1.1.0-preview1, 1.1.0 Oct 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Projects
None yet
Development

No branches or pull requests

6 participants