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

Data Annotation of Keyless Attribute on class can't override in Fluent API #24373

Closed
jryutzy opened this issue Mar 10, 2021 · 8 comments · Fixed by #26143
Closed

Data Annotation of Keyless Attribute on class can't override in Fluent API #24373

jryutzy opened this issue Mar 10, 2021 · 8 comments · Fixed by #26143
Labels
area-conventions 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

@jryutzy
Copy link

jryutzy commented Mar 10, 2021

I have an external table that I want to join up in LINQ. Below works to manually create the relationships between the External Table (SoftwareTools_Software) and TagCategory_X_Software IF in the SoftwareTools_Software class I remove the [Keyless] atrtribute. Is there anyway to remove the [Keyless] attribute using modelBuilder? I've tried all kinds of things and if the Keyless attribute is on my class I get this error. I don't want to manually remove from the code that was auto-generated by scaffolding the DB because the next time we scaffold I'd have to remove it again. There should be a way to overwrite the data annotation from modelBuilder. I've tried modelBuilder.HasAnnotation("Keyless", false); and that doesn't seem to work. Keep getting this error.

Message:

    System.NullReferenceException : Object reference not set to an instance of an object.
  Stack Trace: 
    InternalEntityTypeBuilder.SetOrAddForeignKey(ForeignKey foreignKey, InternalEntityTypeBuilder principalEntityTypeBuilder, IReadOnlyList`1 dependentProperties, Key principalKey, String propertyBaseName, Nullable`1 isRequired, Nullable`1 configurationSource)
    InternalEntityTypeBuilder.CreateForeignKey(InternalEntityTypeBuilder principalEntityTypeBuilder, IReadOnlyList`1 dependentProperties, Key principalKey, String propertyBaseName, Nullable`1 required, ConfigurationSource configurationSource)
.....
namespace Master.App
{
    public partial class TagCategory_X_Software
    {
        [ForeignKey(nameof(SoftwareID_EK))]
        [InverseProperty(nameof(SoftwareTools_Software.TagCategory_X_Softwares))]
        public virtual SoftwareTools_Software SoftwareID_EKNavigation { get; set; }
    }
}

namespace CSUMaster.Ext
{
    [MetadataType(typeof(SoftwareTools_SoftwareMetadata))]
    public partial class SoftwareTools_Software
    {
        public SoftwareTools_Software()
	{
            TagCategory_X_Softwares = new HashSet<TagCategory_X_Software>();
        }

        [InverseProperty(nameof(TagCategory_X_Software.SoftwareID_EKNavigation))]
        public virtual ICollection<TagCategory_X_Software> TagCategory_X_Softwares { get; set; }

        internal sealed class SoftwareTools_SoftwareMetadata
	{
            [Key]
            //[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public long SoftwareID { get; set; }
        }
    }

public partial class MasterContext
	{

		partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
		{
			modelBuilder.Entity<TagCategory_X_Software>(entity =>
			{
				// foregin key relationship back to SoftwareTools_Software
				entity.HasOne(d => d.SoftwareID_EKNavigation)
				   .WithMany(p => p.TagCategory_X_Softwares)
				   .HasForeignKey(d => d.SoftwareID_EK)
				   .OnDelete(DeleteBehavior.ClientSetNull);
			});

			modelBuilder.Entity<SoftwareTools_Software>(entity =>
			{
				entity.HasKey(e => e.SoftwareID);
			});
		}
	}

Similar issue as #20292 and #24283

@smitpatel
Copy link
Contributor

Use HasKey to define primary for the entity type. That will override KeylessAttribute configuration.

@jryutzy
Copy link
Author

jryutzy commented Mar 11, 2021

Hmm... I think that is what I was trying to do here but for some reason it doesn't work. I set a breakpoint and the code throws the error I showed before this code even fires in the OnModelCreatingPartial method.
modelBuilder.Entity<SoftwareTools_Software>(entity =>
{
entity.HasKey(e => e.SoftwareID);
});

@ajcvickers
Copy link
Member

@jryutzy If the code is being scaffolded with [Keyless], then that means EF Core is not finding a suitable key in the database schema. It would be good to understand why this is happening if SoftwareID is a suitable key for this type. Can you post the schema for the two tables involved here so we can look into why the entity type is being scaffolded as keyless?

@jryutzy
Copy link
Author

jryutzy commented Mar 11, 2021

The SoftwareTools_Software table is an External table in SQL Azure DB and as such cannot have a Primary Key. However, a TagCategory_X_Software has an External Key that maps to what would the Primary Key if we could create one on the External table.

I created Navigation Properties on the TagCategory_X_Software and SoftwareTools_Software partial class
image

This allows me to do this
var list = _tagCategoryService.CSUMasterRepository.CSUMasterContext.TagCategory_X_Softwares .Include(x => x.SoftwareID_EKNavigation) .Where(x => x.Active == true) .ToList();
As long as I comment out the [Keyless] attribute on the external table as shown below
image

Here is the log showing that EF is in fact joining these two tables.
image

I don't want to keep manually removing this attribute ever time I scaffold the db.

If I don't remove the [Keyless] attribute I get the error shown in original post and I can't override with Fluent API as it throws the error before the OnModelCreatingPartial method fires.

@ajcvickers
Copy link
Member

@jryutzy Thanks, that makes sense; we'll look into the null-ref bug.

Note for triage: it seems to me that scaffolding could handle this case by introducing a key into the EF model based on the properties being at the principal end of the relationship.

@ajcvickers ajcvickers added this to the 6.0.0 milestone Mar 16, 2021
@ajcvickers
Copy link
Member

See #24369 for updating scaffolding to handle this case.

@slavanap
Copy link

@jryutzy have you tried this?

modelBuilder.Entity<SoftwareTools_Software>(entity =>
{
    entity.Metadata.IsKeyless = false;
    entity.HasKey(e => e.SoftwareID);
});

@AndriySvyryd
Copy link
Member

Related: #24789

@AndriySvyryd AndriySvyryd added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Sep 22, 2021
@AndriySvyryd AndriySvyryd removed their assignment Sep 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-conventions 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.

5 participants