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

Many-to-One relationship with primary key must be required #13391

Closed
ice1e0 opened this issue Sep 22, 2018 · 1 comment
Closed

Many-to-One relationship with primary key must be required #13391

ice1e0 opened this issue Sep 22, 2018 · 1 comment

Comments

@ice1e0
Copy link

ice1e0 commented Sep 22, 2018

I want to set up an N-Hierarchy entity as follows:

public class MyEntry
{
	internal static void OnModelCreating(ModelBuilder modelBuilder)
	{
		modelBuilder.Entity<MyEntry>(entry =>
		{
			entry.ToTable("MyEntry");

			entry.HasKey(a => new { a.CompanyNum, a.Num })
				.HasName("PK_MyEntry");

			entry.HasOne(t => t.Company)
				.WithMany()
				.HasForeignKey(t => t.CompanyNum)
				.HasConstraintName("FK_MyEntry_Company_Num")
				.OnDelete(DeleteBehavior.Restrict);

			entry.HasOne(t => t.Parent)
				.WithMany()
				.HasForeignKey(t => new { t.CompanyNum, t.ParentNum })
				.HasConstraintName("FK_MyEntry_Parent_Num")
				.OnDelete(DeleteBehavior.Restrict);

			entry.HasMany(t => t.Children)
				.WithOne()
				.HasForeignKey(t => new {t.CompanyNum, t.Num})
				.HasPrincipalKey(t => new {t.CompanyNum, t.ParentNum})
				.IsRequired(false);
		});
	}

	[Required(AllowEmptyStrings = false)]
	[MaxLength(10)]
	public string CompanyNum { get; set; }

	public virtual Company Company { get; set; }

	[Required(AllowEmptyStrings = false)]
	[MaxLength(10)]
	public string Num { get; set; }

	[Required]
	[MaxLength(80)]
	public string Description { get; set; }

	[MaxLength(10)]
	public string ParentNum { get; set; }

	public virtual MyEntry Parent { get; set; }

	public virtual List<MyEntry> Children { get; set; }
}

I want to have the field ParentNum nullable because it is not required; at the same time I want to use the Children list field via the foreign key.
The problem is that ef core requires all fields from the foreign key being NOT NULL (or [Required]). When I want to enforce this by using the fluent API with .IsRequired(false) I get an exception (see below).

My expected behavior of the fluent API would be that if I use .isRequired(false) on a relationship, all fields are "optional required" (meaning: they are not required but can be made required by other conditions e.g. being a primary key or being defined with the attribute [Required]).

An alternative solution would be to add support to set a condition to the foreign key (similar to #10665).
Then I could just set a condition that the foreign key only applies when the ParentNum is not NULL.

Exception message: System.InvalidOperationException: The property 'CompanyNum' on entity type 'MyEntry' cannot be marked as nullable/optional because it has been included in a key {'CompanyNum', 'Num'}.
Stack trace:
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalPropertyBuilder.IsRequired(Boolean isRequired, ConfigurationSource configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.IsRequired(Boolean isRequired, ConfigurationSource configurationSource)
   at Microsoft.EntityFrameworkCore.Metadata.Builders.ReferenceCollectionBuilder`2.IsRequired(Boolean required)
   at DuckAccounting.DataModel.Entites.MyEntry.<>c.<OnModelCreating>b__0_0(EntityTypeBuilder`1 entry)
   at Microsoft.EntityFrameworkCore.ModelBuilder.Entity[TEntity](Action`1 buildAction)
   at DuckAccounting.DataModel.Entites.MyEntry.OnModelCreating(ModelBuilder modelBuilder)
   at DuckAccounting.DataModel.DuckAccountingContext.OnModelCreating(ModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelCustomizer.Customize(ModelBuilder modelBuilder, DbContext context)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator)
   at System.Lazy`1.CreateValue()
   at System.Lazy`1.LazyInitValue()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   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.Internal.InternalAccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)

Further technical details

EF Core version: 2.1.2
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 8.1
IDE: Visual Studio 2017 15.6.6

@ajcvickers
Copy link
Member

@ice1e0 Currently alternate keys have the same requirements as primary keys in that they cannot be nullable. Issue #4415 is tracking relaxing that requirement, so this is essentially a duplicate of that issue.

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
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

2 participants