简体   繁体   中英

Can I create a bidirectional foreign key relationship using Entity Framework Code First data annotations?

I'm using EF 6 Code First and trying to figure out the best way to configure my relationships using data annotations, but for some reason EF is adding extra columns to the schema that I don't want.

I have two entities: Ship and Voyage . A ship can have many voyages; a voyage belongs to one and only one ship. So I started out with this (simplified for SO purposes):

public class Ship
{
    public int Id { get; set; }
    public virtual ICollection<Voyage> Voyages { get; set; }
}

public class Voyage
{
    public int Id { get; set; }
    public int ShipId { get; set; }
    public virtual Ship Ship { get; set; }
    public DateTimeOffset Started { get; set; }
}

This creates two tables in the database, as follows:

CREATE TABLE [dbo].[Ship] (
    [Id]                INT                IDENTITY (1, 1) NOT NULL,
    CONSTRAINT [PK_dbo.Ship] PRIMARY KEY CLUSTERED ([Id] ASC)
);

CREATE TABLE [dbo].[Voyage] (
    [Id]               INT                IDENTITY (1, 1) NOT NULL,
    [ShipId]           INT                NOT NULL,
    [Started]          DATETIMEOFFSET (7) NOT NULL,
    CONSTRAINT [PK_dbo.Voyage] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_dbo.Voyage_dbo.Ship_ShipId] FOREIGN KEY ([ShipId]) REFERENCES [dbo].[Ship] ([Id]) ON DELETE CASCADE
);

So far, so good. The problem comes when I want to query this data. I need to obtain a list of ships, but for each ship I want the most recent voyage as well. I couldn't see a way of writing a LINQ query that would do this in one hit, even though I would be able to write such a query using SQL.

My options at this point seemed to be:

  • Load all ships, and for each ship eagerly load all of its voyages. I didn't want to do this because of the performance implications (each ship may ultimately have lots of voyages)
  • Load all ships first, then foreach over the resulting list and perform a separate query for each ship to load its "most recent" voyage. Although this works it seems kind of inefficient compared to a single query.

I then thought I could add a navigation property to the Ship entity that would be used to reference directly the "most recent" voyage. So I tried this:

public class Ship
{
    public int Id { get; set; }
    public virtual ICollection<Voyage> Voyages { get; set; }
    public int? MostRecentVoyageId { get; set; }    <-- new property added
    public virtual Voyage MostRecentVoyage { get; set; }    <-- new property added
}

I made MostRecentVoyageId nullable because a ship will not have any voyages at all when it is first created.

This gives me the following in the database:

CREATE TABLE [dbo].[Ship] (
    [Id]                  INT                IDENTITY (1, 1) NOT NULL,
    [MostRecentVoyageId]  INT                NULL,
    CONSTRAINT [PK_dbo.Ship] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_dbo.Ship_dbo.Voyage_MostRecentVoyageId] FOREIGN KEY ([MostRecentVoyageId]) REFERENCES [dbo].[Voyage] ([Id])
);

which is fine for the Ship table, but I get this for the Voyage table:

CREATE TABLE [dbo].[Voyage] (
    [Id]               INT                IDENTITY (1, 1) NOT NULL,
    [ShipId]           INT                NOT NULL,
    [Started]          DATETIMEOFFSET (7) NOT NULL,
    [Ship_Id]          INT                NULL,
    CONSTRAINT [PK_dbo.Voyage] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_dbo.Voyage_dbo.Ship_ShipId] FOREIGN KEY ([ShipId]) REFERENCES [dbo].[Ship] ([Id]) ON DELETE CASCADE,
    CONSTRAINT [FK_dbo.Voyage_dbo.Ship_Ship_Id] FOREIGN KEY ([Ship_Id]) REFERENCES  [dbo].[Ship] ([Id])
);

Note the extra Ship_Id column and the additional foreign key relationship. I'm pretty certain that this column isn't needed, but I can't find a way of getting rid of it. I've tried using the [ForeignKey] and [InverseProperty] attributes but these just give me an exception.

Is this the kind of relationship I can't configure using EF data annotations? Do I have to use the fluent syntax for this? Am I doing it all wrong anyway: is there a better way to do the LINQ queries using my original entity types?

I have tried looking on SO for people having similar problems and I found this , this and this but none of those seemed to help.

我有几乎完全相同的模型,但是在不同的上下文中,并且通过将[ForeignKey("MostRecentVoyage")]属性添加到MostRecentVoyageId并添加[InverseProperty("Ship")]摆脱了数据库中不需要的列都在Ship实体/类上同时具有Voyages属性。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM