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

Cannot set Foreign Key column name by Fluent API #6880

Closed
cdie opened this issue Oct 27, 2016 · 7 comments
Closed

Cannot set Foreign Key column name by Fluent API #6880

cdie opened this issue Oct 27, 2016 · 7 comments
Assignees
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Milestone

Comments

@cdie
Copy link

cdie commented Oct 27, 2016

I'm trying to give my Foreign Key column the name I want (not talking about the constraint name).

If I set my foreignKey with the builder.Property,

var b = builder.Property(column.PropertyType, column.PropertyName);
b.ForSqlServerHasName("MyFKColumnName");

when I create relationship, I have an error on migration :
"The navigation property 'LettreClePrincipale' cannot be added to the entity type 'LettreCleAssociation' because a property with the same name already exists on entity type 'LettreCleAssociation'."

If I don't set my foreignKey with the builder.Property, this error is gone, the key is correctly created, but I cannot set name during the process :


builder.HasMany(fks.DistantObjectType.FullName, fks.CurrentPropertyName)
.WithOne().IsRequired(fks.Required)
.OnDelete(fks.DeleteCascade ? Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Cascade : Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior)
.HasForeignKey(keyStorageOfProp.Name).???

I don't have ForSqlServerHasName or ForSqliteHasName extensions method here. How can I proceed to achieve this ?

@rowanmiller
Copy link
Contributor

Can you share a full code listing (or project) that we can run to debug the issue? There isn't enough info here for us to work out what is going on.

@cdie
Copy link
Author

cdie commented Nov 2, 2016

Considering this architecture

class LTC
        {
            public int TestA { get; set; }
            public string TestB { get; set; }

            public Guid Id { get; set; }

            protected ICollection<LTCLinks> _links { get; set; }

            public IReadOnlyList<LTCLinks> Links
            {
                get
                {
                    return _links.ToList().AsReadOnly();
                }
            }
        }

        class LTCLinks
        {
            public LTC Link1 { get; set; }
            public LTC Link2 { get; set; }

            public string Data { get; set; }
        }

with this configuration

                var ltcBuilder = modelBuilder.Entity(typeof(LTC));
                ltcBuilder.Property(typeof(int), "TestA").IsRequired();
                ltcBuilder.Property(typeof(string), "TestB").IsRequired();
                ltcBuilder.HasKey("Id");
                ltcBuilder.Ignore("_links");


                var ltcLinkBuilder = modelBuilder.Entity(typeof(LTCLinks));
                ltcBuilder.Property(typeof(string), "Data").IsRequired();

                ltcLinkBuilder.HasOne(typeof(LTC).FullName, "Link1").WithMany().IsRequired().OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Cascade);
                ltcLinkBuilder.HasOne(typeof(LTC).FullName, "Link2").WithMany().IsRequired(false).OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);

                ltcLinkBuilder.HasKey("Data", "Link1ID", "Link2ID");

The two foreign keys for LTCLinks are created as shadow properties by EF. How can I define their name in fluent API ?
I've found a workaround for my case by creating explicit foreign key and giving they a name by using using the entityBuilder.Property method. I think it's the good way, but I want to have a workaround for my DAL if the final dev has not created the foreign key.

@rowanmiller rowanmiller added this to the 1.2.0 milestone Nov 2, 2016
@divega
Copy link
Contributor

divega commented Nov 3, 2016

I've found a workaround for my case by creating explicit foreign key and giving they a name by using using the entityBuilder.Property method.

@cdie I am not sure if by "creating explicit foreign key" you are referring to defining a shadow property explicitly with a call like entityBuilder.Property<Guid>("Link1Id") or if you are adding actual C# properties to the LTCLinks type. Either approach should work, but I would also recommend adding a call to HasForeignKey() indicating what property to use for each relationship to avoid any ambiguity.

I am also curious about some things you are doing in your code, e.g. why not use the generic versions of the model building methods and lambda expressions to identify properties? It can lead to easier to read code.

@divega divega removed this from the 1.2.0 milestone Nov 3, 2016
@divega divega removed their assignment Nov 3, 2016
@cdie
Copy link
Author

cdie commented Nov 3, 2016

@divega when I said "creating explicit foreign key", I was talking about adding 2 Guids C# properties for defining foreign keys for Link1& Link2object. In fact, these properties are not mandatory for EF to work, as I said in my first post, EF create shadow properties for them if we don't add them.
If the final developper in our company forget to create those 2 Guids properties, I don't know how to set the foreign key name. And yes, indeed, in my workaround, because I created these two properties, I can use the HasForeignKey method. However, same question, how can I do that if it's EF that create shadow properties for me ?
This is more curiousity, because I'll try to force our devs to create foreign key properties by throwing exception during migration creation.

About the using of generic versions, the code above was just a little example of how when proceed, because our architecture was previously used without EF, so we've defined our own attributes to create migrations automaticly by reflection. So I created an example and adapted it to try to retranscript at best what we're doing by reflection for our own business objects. I wanna be sure that it's (or not) possible to fix foreign key name with non generics methods, so this is why I designed such example.

@divega divega self-assigned this Nov 4, 2016
@divega divega added this to the 1.2.0 milestone Nov 4, 2016
@ajcvickers ajcvickers modified the milestones: 2.0.0-preview1, 2.0.0 Apr 19, 2017
@smitpatel
Copy link
Member

How to name the FK column

  • If you are having a property in CLR type which is being used as FK property then easiest way to do is to use Property().HasColumnName()
    e.g. modelBuilder.Entity<LTCLinks>().Property(e => e.Link1Id).HasColumnName("MyName");

  • If you are not having a property in CLR type then you would have a shadow property for FK column. Now you can either name your shadow property as the column name you want or leave the name of FK by convention and explicitly name it to something different. Keep in mind that while the conventional FK name remains the same but the name depends on the model due to clash in the name between multiple relationships. So it may not possible for you to compute the exact name for every model easily and it is not advisable to go that path. Easiest way is the former - Name your shadow FK the name you want in database. This way you don't have to have explicit CLR property still you can control the name.
    e.g. builder.Entity<LTC>().HasMany<LTCLinks>().WithOne(l => l.Link1).HasForeignKey("MyName");

@smitpatel
Copy link
Member

@divega - Should we close this issue now?
The issue was created before 1.1 release. It seems that .Property method was used on navigation property to configure the name but then later used in relationship API hence the exception thrown. We have improved a lot of more checks in metadata API to avoid such incorrect model building.
Also, I responded above how to change the name of FK property if user wants to.

@divega
Copy link
Contributor

divega commented Jul 7, 2017

@smitpatel thanks for poaching.

@divega divega closed this as completed Jul 7, 2017
@divega divega added closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug and removed type-investigation labels Jul 7, 2017
@divega divega modified the milestones: 1.1.0, 2.0.0 Jul 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Projects
None yet
Development

No branches or pull requests

5 participants