-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Support for expressions when configuring composite keys #33114
Comments
You can configure composite keys like this
Are you asking for something different to that? |
Sorry, I could have explained this more clearly. I'm specifically talking about the scenario where the composite key includes columns which are part of a foreign key relationship, so the key properties are actually members of a related entity. Given these entities: public class Venue {
public Guid Id { get; set; }
public string Name { get; set; } = String.Empty;
}
public class Artist {
public Guid Id { get; set; }
public string Name { get; set; } = String.Empty;
}
public class Show {
public Venue Venue { get; set; } = default!;
public DateOnly Date { get; set; }
public Artist HeadlineArtist { get; set; } = default!;
public List<SupportSlot> SupportSlots { get; set; } = [];
} The primary key for modelBuilder.Entity<Show>().HasKey(show => new { show.Venue.Id, show.Date }); throws an
To extend it one step further, the public class SupportSlot {
public Show Show { get; set; } = default!;
public int SlotNumber { get; set; }
public Artist Artist { get; set; } = default!;
} In this example, Using the anonymous object syntax: modelBuilder.Entity<SupportSlot>().HasKey(slot => new { slot.Show.Venue.Id, slot.Show.Date, slot.SlotNumber }); throws an exception:
because it doesn't translate the expression Consequently, the only way (as far as I can see) to handle this kind of key in EF Core is to use string column names - but I don't see any reason it couldn't support using indirect member access expressions, either via the Hence the question: would there be interest in a PR adding support for either/both of these syntaxes? Thanks :) |
I'm not following here: is Venue mapped to a different table? If so, how can its Id column (in table If Venue is mapped to the same table as Shows - as an owned entity or cmoplex type - then this is a variation on #11336 (which is about doing the same for indexes). |
Each entity here is mapped to its own table; I'm not using owned entities or complex types. The DB schema diagram looks like this:
Yes, but those columns can be part of a foreign key. Every I've created a complete repro which might illustrate the issue more clearly; the DB schema diagram above is the actual database created by running this code: https://github.com/dylanbeattie/efcore-composite-key-examples For what it's worth, EF Core is doing exactly the right thing here: the database schema is correct, the foreign key relationships are correct; my frustration is with having to rely on string column names to make this happen: modelBuilder.Entity<Show>().HasKey("VenueId", "Date");
modelBuilder.Entity<SupportSlot>().HasKey("ShowVenueId", "ShowDate", "SlotNumber"); I'm proposing extending |
@dylanbeattie I'm trying to understand how this would work. Can you write an example of the complete |
Sure - the code I'd like to be able to write is either of the following syntaxes: Syntax option 1: multiple expressions protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Show>().HasKey(
show => show.Venue.Id,
show => show.Date
);
modelBuilder.Entity<SupportSlot>().HasKey(
slot => slot.Show.Venue.Id,
slot => slot.Show.Date,
slot => slot.SlotNumber
);
modelBuilder.Entity<Show>()
.HasMany(show => show.SupportSlots)
.WithOne(slot => slot.Show).OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Artist>()
.HasMany(artist => artist.HeadlineShows)
.WithOne(slot => slot.HeadlineArtist).OnDelete(DeleteBehavior.SetNull);
modelBuilder.Entity<Artist>()
.HasMany(artist => artist.SupportSlots)
.WithOne(slot => slot.Artist).OnDelete(DeleteBehavior.SetNull);} Syntax option 2: anonymous type with multiple expressions protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Show>().HasKey(show => new {
show.Venue.Id,
show.Date
});
modelBuilder.Entity<SupportSlot>().HasKey(slot => new {
slot.Show.Venue.Id,
slot.Show.Date,
slot.SlotNumber
});
modelBuilder.Entity<Show>()
.HasMany(show => show.SupportSlots)
.WithOne(slot => slot.Show).OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Artist>()
.HasMany(artist => artist.HeadlineShows)
.WithOne(slot => slot.HeadlineArtist).OnDelete(DeleteBehavior.SetNull);
modelBuilder.Entity<Artist>()
.HasMany(artist => artist.SupportSlots)
.WithOne(slot => slot.Artist).OnDelete(DeleteBehavior.SetNull);
} |
modelBuilder.Entity<Show>().HasKey(show => new {
show.Venue.Id,
show.Date
}); Are you basically asking to be able to define a key column on Shows - which happens to also be a foreign key pointing to Values - by referencing the column on Values which that foreign key is pointing to? For one thing, what happens if there are more than one columns on Show pointing to the same key column on Venue, how would we know which one to resolve it to? Even without that problem, it seems very odd (and confusing) to me to use a destination key property as a way to configure the source foreign key. If all this is only to avoid using string property names, you always have the option of having a CLR property for the foreign key, at which point you can simply use that, just like you're using the key itself. |
I agree with @roji If the purpose of this proposal is to avoid depending on the FK property names generated by EF than it can be accomplished by explicitly configuring them before configuring the PK. |
EF Core currently doesn't support configuring composite keys using expressions.
For a "normal" (atomic?) key, you can specify:
or
Consider this schema: a
Venue
is defined by a regularId
. AShow
is defined by the combination of aVenue
and aShowDate
(can't have two shows at the same venue on the same day). EachShow
can have zero or more numberedSupportSlot
entities, so eachSupportSlot
is identifed by(show, slotNumber)
-- butshow
is identified by(venue, date)
andvenue
is identified byId
Currently, the only way to configure this composite key is via string column names:
What I'd like to do is:
This isn't natively supported in EF Core, but I have an extension method which I use for this on my own projects:
Would there be interest in a PR to add this code to EF Core?
The text was updated successfully, but these errors were encountered: