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

Translate GetType() for entity types with inheritance #13424

Closed
subprime opened this issue Sep 27, 2018 · 11 comments · Fixed by #28011
Closed

Translate GetType() for entity types with inheritance #13424

subprime opened this issue Sep 27, 2018 · 11 comments · Fixed by #28011
Assignees
Labels
area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Milestone

Comments

@subprime
Copy link

In my queries i want achive that all vehicles except the prototype vehicles are returned, which has more properties. The query is build with OfType() but it seems that this has no influence of the behaviour.

public abstract class BaseVehicle
{
    public int Id { get; set; }
}

public class Vehicle : BaseVehicle
{
    public string VehicleIdentificationNumber { get; set; }
}

public class VehiclePrototype : Vehicle
{
    public DateTimeOffset? PressReleaseAt { get; set; }
}

Is this normal? What is the right way to filtrer the entities?

Further technical details

EF Core version: 2.1
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10
IDE: Visual Studio 2017 Pro 15.8.4

@ajcvickers
Copy link
Member

@subprime The semantics of OfType is "that type or any derived type", like is or as in C#. This is not an EF thing, but rather how OfType is defined in LINQ.

That being said, I don't know the best way to get just entities of a specific type in EF Core. I tried:

context.Set<BaseVehicle>().Where(v => v.GetType() == typeof(Vehicle)).Cast<Vehicle>().ToList();

but that evaluates on the client. This:

context.Set<BaseVehicle>().Where(v => EF.Property<string>(v, "Discriminator") == nameof(Vehicle)).Cast<Vehicle>().ToList();

works, but it's a bit obscure.

@smitpatel Can you advise?

@subprime
Copy link
Author

Hey @ajcvickers thanks for the fast reply. I know what you mean but it's wierd. My interpretion of OfType was to get the concrete type instead of the derived types because they have different endpoints in my api... 👎

@ajcvickers
Copy link
Member

@subprime Can you be more specific about what is weird about it? This is the way OfType is defined in the base class libraries for .NET. If you feel this is wrong, then probably the compiler team is the best place to give that feedback.

@smitpatel
Copy link
Member

@subprime - Please post your query.

@ajcvickers
Copy link
Member

@smitpatel Not sure which query you want, but here are the three I tested, with the repro code below.

Using OfType:

context.Set<BaseVehicle>().OfType<Vehicle>()
SELECT [b].[Id], [b].[Discriminator], [b].[VehicleIdentificationNumber], [b].[PressReleaseAt]
FROM [BaseVehicle] AS [b]
WHERE [b].[Discriminator] IN (N'VehiclePrototype', N'Vehicle')

Using GetType:

context.Set<BaseVehicle>()
    .Where(v => v.GetType() == typeof(Vehicle)).Cast<Vehicle>()
SELECT [v].[Id], [v].[Discriminator], [v].[VehicleIdentificationNumber], [v].[PressReleaseAt]
FROM [BaseVehicle] AS [v]
WHERE [v].[Discriminator] IN (N'VehiclePrototype', N'Vehicle')

Using discriminator:

context.Set<BaseVehicle>()
    .Where(v => EF.Property<string>(v, "Discriminator") == nameof(Vehicle)).Cast<Vehicle>()
SELECT [v].[Id], [v].[Discriminator], [v].[VehicleIdentificationNumber], [v].[PressReleaseAt]
FROM [BaseVehicle] AS [v]
WHERE [v].[Discriminator] IN (N'VehiclePrototype', N'Vehicle') AND ([v].[Discriminator] = N'Vehicle')

Full code:

public abstract class BaseVehicle
{
    public int Id { get; set; }
}

public class Vehicle : BaseVehicle
{
    public string VehicleIdentificationNumber { get; set; }
}

public class VehiclePrototype : Vehicle
{
    public DateTimeOffset? PressReleaseAt { get; set; }
}

public class BloggingContext : DbContext
{
    private static readonly LoggerFactory Logger
        = new LoggerFactory(new[] { new ConsoleLoggerProvider((_, __) => true, true) });

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseLoggerFactory(Logger)
            .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BaseVehicle>();
        modelBuilder.Entity<Vehicle>();
        modelBuilder.Entity<VehiclePrototype>();
    }
}

public class Program
{
    public static async Task Main()
    {
        using (var context = new BloggingContext())
        {
            await context.Database.EnsureDeletedAsync();
            await context.Database.EnsureCreatedAsync();

            context.Add(new Vehicle());
            context.Add(new VehiclePrototype());
            context.SaveChanges();
        }

        using (var context = new BloggingContext())
        {
            var vehicles = context.Set<BaseVehicle>().Where(v => v.GetType() == typeof(Vehicle)).Cast<Vehicle>().ToList();
        }
    }
}

@subprime
Copy link
Author

subprime commented Oct 1, 2018

@smitpatel which query you want?

@smitpatel
Copy link
Member

In my queries i want achive that all vehicles except the prototype vehicles are returned

OfType is certainly not the linq operator which maps to that behavior. Hence, I am having hard time understanding what is exact linq query you are running which uses OfType and pulls all data which you don't want.

@smitpatel
Copy link
Member

Posting your original attempted query and description would help us get you in write direction about what query should be written to work efficiently with EF.

@smitpatel
Copy link
Member

Triage decision: v.GetType() == typeof(Vehicle) should get converted to discriminator predicate when used with TPH mapping.

@subprime
Copy link
Author

@smitpatel, sorry i was in holiday...
Can you shortly explain what this means?

v.GetType() == typeof(Vehicle) should get converted to discriminator predicate when used with TPH mapping.

@smitpatel
Copy link
Member

We will convert v.Getype() == typeof(Vehicle) to EF.Property(v, "Discriminator) == "Vehicle" automatically to do server evaluation.

@ajcvickers ajcvickers modified the milestones: 3.0.0, Backlog Jan 24, 2019
@smitpatel smitpatel removed their assignment Jan 29, 2019
@AndriySvyryd AndriySvyryd changed the title Multiple inherance in models return all elements in query instead of specific type Translate Getype() for entity types with inheritance Oct 24, 2019
@AndriySvyryd AndriySvyryd changed the title Translate Getype() for entity types with inheritance Translate GetType() for entity types with inheritance Oct 24, 2019
@smitpatel smitpatel added the good first issue This issue should be relatively straightforward to fix. label Mar 16, 2020
@smitpatel smitpatel removed this from the Backlog milestone May 6, 2022
@AndriySvyryd AndriySvyryd removed the good first issue This issue should be relatively straightforward to fix. label May 10, 2022
@AndriySvyryd AndriySvyryd added this to the 7.0.0 milestone May 10, 2022
smitpatel added a commit that referenced this issue May 12, 2022
smitpatel added a commit that referenced this issue May 12, 2022
@smitpatel smitpatel added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label May 18, 2022
smitpatel added a commit that referenced this issue May 18, 2022
@ghost ghost closed this as completed in #28011 May 18, 2022
ghost pushed a commit that referenced this issue May 18, 2022
@ajcvickers ajcvickers modified the milestones: 7.0.0, 7.0.0-preview5 May 25, 2022
@ajcvickers ajcvickers modified the milestones: 7.0.0-preview5, 7.0.0 Nov 5, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants