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

Checking for a derived type using the "is" operator no longer works #18569

Closed
BickelLukas opened this issue Oct 24, 2019 · 3 comments · Fixed by #23217
Closed

Checking for a derived type using the "is" operator no longer works #18569

BickelLukas opened this issue Oct 24, 2019 · 3 comments · Fixed by #23217
Assignees
Labels
area-in-memory 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-bug
Milestone

Comments

@BickelLukas
Copy link

When querying a DbSet of an abstract type and then checking if the materialized value is of a specific type, the check always returns null.

Steps to reproduce

abstract class Base {
}

class Derived : Base {
    public string Test { get; }
}
from item in context.Set<Base>()
select new {
   Test = item is Derived ? ((Derived) item).Test : "default"
}

When executing the query Test is always set to default

Further technical details

EF Core version: 3.1.0-preview1.19508.20
Database provider: Npgsql.EntityFrameworkCore.PostgreSQL
Target framework: NET Core 3.0
Operating system: Windows 10
IDE: Visual Studio 2019 16.3

@ajcvickers
Copy link
Contributor

@BickelLukas I have not been able to reproduce this--see my code below. Please post a small, runnable project/solution or complete code listing that demonstrates the behavior you are seeing.

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

class Derived : Base
{
    public string Test { get; } = "Cheese";
}

public class BloggingContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Base>();
        modelBuilder.Entity<Derived>();
    }
}

public class Program
{
    public static async Task Main()
    {
        using (var context = new BloggingContext())
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            context.Add(new Derived());
            context.SaveChanges();
        }

        using (var context = new BloggingContext())
        {
            var query = from item in context.Set<Base>()
                select new
                {
                    Test = item is Derived ? ((Derived) item).Test : "default"
                };

            var results = query.ToList();
        }
    }
}

@BickelLukas
Copy link
Author

@ajcvickers

    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new BloggingContext())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();

                context.Add(new Derived("Cheese"));
                context.SaveChanges();
            }

            using (var context = new BloggingContext())
            {
                var query = (from item in context.Set<Base>()
                             select new
                             {
                                 Test = item is Derived ? ((Derived)item).Test : null,
                                 Test2 = item is Derived,
                             });

                var results = query.ToList(); // Test = null; test2 = false
            }
        }
    }


    public abstract class Base
    {
        public int Id { get; set; }
        public Metadata Metadata { get; set; } = new Metadata();
    }

    public class Metadata
    {
        public DateTimeOffset Created { get; set; }
    }

    public class Derived : Base
    {
        public Derived(string test)
        {
            Test = test;
        }

        public string Test { get; private set; }
    }

    public class BloggingContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseInMemoryDatabase("test");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Base>().OwnsOne(x => x.Metadata);
            modelBuilder.Entity<Derived>();
        }
    }

The problem starts occurring when the base class contains an owned type.
It only happens when using the InMemory Provider (sorry for the confusion, It didn't work for us in the unit tests but actually works when using Npgsql or SqlServer)

@ajcvickers
Copy link
Contributor

@BickelLukas Thanks!

Note for triage: This repros for me with in-memory and the current 3.1 bits. That is, there is no exception, but the results are incorrect.

However, on 3.0, this instead throws an exception, so we already changed something in 3.1 that impacted this.

3.0 exception (not thrown in 3.1)

nhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryExpressionTranslatingExpressionVisitor.VisitUnary(UnaryExpression unaryExpression)
   at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryExpressionTranslatingExpressionVisitor.VisitMember(MemberExpression memberExpression)
   at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryExpressionTranslatingExpressionVisitor.VisitConditional(ConditionalExpression conditionalExpression)
   at System.Linq.Expressions.ConditionalExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryExpressionTranslatingExpressionVisitor.Translate(Expression expression)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.VisitNew(NewExpression newExpression)
   at System.Linq.Expressions.NewExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.Translate(InMemoryQueryExpression queryExpression, Expression expression)
   at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Program.Main(String[] args) in C:\Stuff\ThreeOh\ThreeOh\Program.cs:line 77

@ajcvickers ajcvickers added this to the Backlog milestone Oct 25, 2019
@ajcvickers ajcvickers modified the milestones: Backlog, 6.0.0 Nov 5, 2020
@smitpatel smitpatel self-assigned this Nov 6, 2020
smitpatel added a commit that referenced this issue Nov 6, 2020
@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 Nov 6, 2020
smitpatel added a commit that referenced this issue Nov 9, 2020
@ajcvickers ajcvickers modified the milestones: 6.0.0, 6.0.0-preview1 Jan 27, 2021
@ajcvickers ajcvickers modified the milestones: 6.0.0-preview1, 6.0.0 Nov 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-in-memory 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-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants