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

Improve exception when defining a relationship to a keyless entity type #20292

Closed
seattleite7 opened this issue Mar 15, 2020 · 4 comments
Closed

Comments

@seattleite7
Copy link

seattleite7 commented Mar 15, 2020

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;

namespace EFCoreNullException
{
    class Summary
    {
        [Column("userId")]
        public long UserID { get; set; }
        [Column("hours")]
        public int Hours { get; set; }
        public ContactInfo Contact { get; set; } = new ContactInfo();
    }

    class ContactInfo
    {
        [Column("userId")]
        public long UserID { get; set; }
        [Column("name")]
        public string Name { get; set; } = "";
    }

    class MyContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseMySql("host=localhost;database=test;user id=test;password=test", x => x.ServerVersion("8.0.16-mysql"));
            }
        }
        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<ContactInfo>().HasNoKey();
            builder.Entity<Summary>().HasKey(e => e.UserID);
            builder.Entity<Summary>().HasOne(e => e.Contact).WithOne().HasPrincipalKey<Summary>(e => e.UserID).HasForeignKey<ContactInfo>(b => b.UserID);
            builder.Entity<Summary>().ToTable("summaries");
            builder.Entity<ContactInfo>().ToTable("contacts");
        }
        public DbSet<Summary> Summaries { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyContext context = new MyContext();
            IQueryable<Summary> query = context.Summaries.Where(e => e.UserID == 1);
            Summary t = query.FirstOrDefault();
            Console.WriteLine(t.Contact.Name);
        }
    }
}
System.NullReferenceException: 'Object reference not set to an instance of an object.'  
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.SetOrAddForeignKey(ForeignKey foreignKey, InternalEntityTypeBuilder principalEntityTypeBuilder, IReadOnlyList`1 dependentProperties, Key principalKey, String navigationToPrincipalName, Nullable`1 isRequired, Nullable`1 configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.CreateForeignKey(InternalEntityTypeBuilder principalEntityTypeBuilder, IReadOnlyList`1 dependentProperties, Key principalKey, String navigationToPrincipalName, Nullable`1 isRequired, ConfigurationSource configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.HasRelationship(EntityType targetEntityType, Nullable`1 navigationToTarget, Nullable`1 inverseNavigation, Boolean setTargetAsPrincipal, ConfigurationSource configurationSource, Nullable`1 required)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.HasRelationship(EntityType targetEntityType, MemberInfo navigationProperty, ConfigurationSource configurationSource, Boolean setTargetAsPrincipal)
   at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder`1.HasOne[TRelatedEntity](Expression`1 navigationExpression)
   at EFCoreNullException.MyContext.OnModelCreating(ModelBuilder builder) in C:\Users\Eric\source\repos\EFCoreNullException\Program.cs:line 65
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelCustomizer.Customize(ModelBuilder modelBuilder, DbContext context)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_3(IServiceProvider p)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_Model()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityType()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.CheckState()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityQueryable()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Linq.IQueryable.get_Provider()
   at System.Linq.Queryable.Where[TSource](IQueryable`1 source, Expression`1 predicate)
   at EFCoreNullException.Program.Main(String[] args) in \EFCoreNullException\Program.cs:line 81

Further technical details

EF Core version: Microsoft.EntityFrameworkCore 3.1.2
Database provider: Pomelo.EntityFrameworkCore.MySql 3.1.1
Target framework: .NET Core 3.1.0
Operating system: Windows 10
IDE: Microsoft Visual Studio Community 2019 Version 16.4.2

@seattleite7 seattleite7 changed the title Null Pointer Exception in SetOrAddForeignKey Null Reference Exception in SetOrAddForeignKey Mar 15, 2020
@ajcvickers
Copy link
Member

@seattleite7 What is your intention by using builder.Entity<ContactInfo>().HasNoKey(); here?

@seattleite7
Copy link
Author

@ajcvickers I was going off the error message "The type requires a primary key to be defined. If you intend to use a keyless type, call HasNoKey()". But now I get it has to have a primary key even if the foreign key is unique. Full disclosure, I am very new to EF.

@ajcvickers
Copy link
Member

@seattleite7 Welcome to EF Core! :-)

There's a few ways that you could map these types. Taking the lead from your code, something like this will map the types as normal entity types:

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.Entity<ContactInfo>().HasKey(e => e.UserID);
    builder.Entity<ContactInfo>().ToTable("contacts");

    builder.Entity<Summary>().HasKey(e => e.UserID);
    builder.Entity<Summary>()
        .HasOne(e => e.Contact)
        .WithOne()
        .HasPrincipalKey<Summary>(e => e.UserID)
        .HasForeignKey<ContactInfo>(b => b.UserID);
    
    builder.Entity<Summary>().ToTable("summaries");
}

When writing a query against this, you'll need to use Incldue to load the related ContactInfo:

var query = context.Summaries.Include(e => e.Contact).Where(e => e.UserID == 1);

Another option would be to use an owned type. This is more appropriate if the ContactInfo entity is conceptually part of the Summary aggregate. It means that:

  • ContactInfo will be automatically included when loading a Summary
  • ContactInfo doesn't need to declare it's own key property.

Note that in either case, this code is problematic:

public ContactInfo Contact { get; set; } = new ContactInfo();

This creates a new ContactInfo entity instance every time a Summary is created, which in turn leads to ambiguity if EF loads an entity instance. See #18007 for an in-depth discussion/

@AndriySvyryd AndriySvyryd changed the title Null Reference Exception in SetOrAddForeignKey Improve exception when defining a relationship to a keyless entity type Mar 15, 2020
@ajcvickers ajcvickers added this to the Backlog milestone Mar 16, 2020
@ajcvickers ajcvickers added poachable good first issue This issue should be relatively straightforward to fix. labels Mar 16, 2020
@AndriySvyryd
Copy link
Member

Duplicate of #18149

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants