1

我正在尝试从 Entity Framework 5 的 Code First 中找到一种方法来创建这种关系。

在下面的示例中,我有一些Stock可以属于WarehouseRow

库存表

'Id', 'SerialNo', 'Discriminator', 'LocationId'
345, ABC123, Warehouse, 4  
123, ABC124, Row, 12

仓库表

'Id', 'Name'  
4, WH-One  
8, WH-Two

行表

'Id', 'Name'
6, RowA  
12, RowB

表Stock上的鉴别器列确定LocationId所指的相关表。例如:ID为“345”的库存引用了 Warehouse 表中 ID 为 4 (WH-One )

这种类型的关系可以用 EF Code First 映射吗?在 SQL Server 中查询这种结构当然不成问题。

SELECT Stock.SerialNo, COALESCE(Warehouse.Name, Row.Name) AS Name
FROM Stock
LEFT OUTER JOIN Warehouse ON Stock.Id = Warehouse.Id AND Stock.Discriminator = 'Warehouse'
LEFT OUTER JOIN Row ON Stock.Id = Row.Id AND Stock.Discriminator = 'Row'

哪个应该返回:

SerialNo, Name
ABC123, WH-One
ABC124, RowB

理想情况下,我希望有一个接口或抽象基类,它们是WarehouseRow并从中派生的。还有一个名为Location的导航属性,它将引用转换为抽象/接口的任一表。

或者,我想知道是否有人有更好的建议来描述 EF Code First 中的关系,允许一个实体引用其他几个类似实体之一。

4

1 回答 1

0

这是代码优先所以我会这样做:

首先- 在代码中创建我想要的类:

public abstract class Location
{
    public int ID { get; set; }
    public string Name { get; set; }
    public ICollection<Stock> StockItems { get; set; }
}

public class Warehouse : Location
{
    public int NumberOfLoadingDocks { get; set; }
}

public class Row : Location
{
    public int NumberOfShelves { get; set; }
}

public class Stock
{
    public int ID { get; set; }
    public string SerialNo { get; set; }
    public int LocationID { get; set; }
}

然后将它们添加到上下文中:

public partial class MyContext : DbContext
{
    public DbSet<Location> Locations { get; set; }
    public DbSet<Warehouse> Warehouses { get; set; }
    public DbSet<Row> Rows { get; set; }
    public DbSet<Stock> Stocks { get; set; }
}

然后在包管理器控制台中:

Add-Migration "Locations"
Update-Database -Script

这让我看到了 Entity Framework 将创建的表:

CREATE TABLE [dbo].[Stocks] (
    [ID] [int] NOT NULL IDENTITY,
    [SerialNo] [nvarchar](max),
    [LocationID] [int] NOT NULL,
    CONSTRAINT [PK_dbo.Stocks] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_LocationID] ON [dbo].[Stocks]([LocationID])
CREATE TABLE [dbo].[Locations] (
    [ID] [int] NOT NULL IDENTITY,
    [Name] [nvarchar](max),
    [NumberOfShelves] [int],
    [NumberOfLoadingDocks] [int],
    [Discriminator] [nvarchar](128) NOT NULL,
    CONSTRAINT [PK_dbo.Locations] PRIMARY KEY ([ID])
)
ALTER TABLE [dbo].[Stocks] ADD CONSTRAINT [FK_dbo.Stocks_dbo.Locations_LocationID] FOREIGN KEY ([LocationID]) REFERENCES [dbo].[Locations] ([ID])
INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion])
VALUES (N'201309120832257_Locations'....

与您拥有的表不同。这是Table-per-Hierarchy 继承。如果您对此不满意,那么您需要查看Table Per Type 继承

编辑 要获得 TPT 继承,您可以将 Table 属性添加到继承的类中,如下所示:

   [System.ComponentModel.DataAnnotations.Schema.Table("Warehouses")]
    public class Warehouse : Location
    {
        public int NumberOfLoadingDocks { get; set; }
    }

    [System.ComponentModel.DataAnnotations.Schema.Table("Rows")]
    public class Row : Location
    {
        public int NumberOfShelves { get; set; }
    }

现在您可以为每种类型获得单独的表格:

CREATE TABLE [dbo].[Locations] (
    [ID] [int] NOT NULL IDENTITY,
    [Name] [nvarchar](max),
    CONSTRAINT [PK_dbo.Locations] PRIMARY KEY ([ID])
)
CREATE TABLE [dbo].[Rows] (
    [ID] [int] NOT NULL,
    [NumberOfShelves] [int] NOT NULL,
    CONSTRAINT [PK_dbo.Rows] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_ID] ON [dbo].[Rows]([ID])
CREATE TABLE [dbo].[Warehouses] (
    [ID] [int] NOT NULL,
    [NumberOfLoadingDocks] [int] NOT NULL,
    CONSTRAINT [PK_dbo.Warehouses] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_ID] ON [dbo].[Warehouses]([ID])
ALTER TABLE [dbo].[Stocks] ADD CONSTRAINT [FK_dbo.Stocks_dbo.Locations_LocationID] FOREIGN KEY ([LocationID]) REFERENCES [dbo].[Locations] ([ID])
ALTER TABLE [dbo].[Rows] ADD CONSTRAINT [FK_dbo.Rows_dbo.Locations_ID] FOREIGN KEY ([ID]) REFERENCES [dbo].[Locations] ([ID])
ALTER TABLE [dbo].[Warehouses] ADD CONSTRAINT [FK_dbo.Warehouses_dbo.Locations_ID] FOREIGN KEY ([ID]) REFERENCES [dbo].[Locations] ([ID])

如果您对此不满意,您应该查看Table Per Concrete Type Inheritance ...

于 2013-09-12T09:08:49.937 回答