[Entity Framework][Code First]Mapping a Single Entity Across Multiple Tables

Entity Splitting

前言

繼上一篇文章將兩個Entity對應到同一個Table,反之,使用Code First將多個Table對應到一個Entity也是可行的。

 

範例:

目前有一個Student如下,並且打算將Address,Zip欄位的資料存在另一個Table。

public class Student
{
    public int Id { get; set; }

    public int SerialNumber { get; set; }

    public string Name { get; set; }
    
    //Enum型別
    public Gender Gender { get; set; }

    public byte[] Photo { get; set; }
    
    public string Address { get; set; }

    public string Zip { get; set; }
}

要做到Entity Splitting,目前無法用Data Annotation,只能用Fluent API。

使用Map的方式來將欄位分成兩個Table,如下:

public class StudentConfiguration : EntityTypeConfiguration<Student>
{
    public StudentConfiguration()
    {
        HasKey(x => x.Id);            
        Map(m => 
            {
                m.Properties(d => new { d.Name, d.SerialNumber, d.Photo, d.Gender });
                m.ToTable("StudentInformation");
            }
        ).Map(m =>
        {
            m.Properties(d => new { d.Address, d.Zip });
            m.ToTable("StudentAddress");
        });        
    }
}

產生的資料表如下:

說明:

1.這兩個Table共享一個Primary Key,且我Map的時候是先寫Map StudentInformation再 Map StudentAddress,所以StudentAddress會建立Foreign Key依賴於StudentInformation。

2.如果未將所有欄位Map完,Code First會將未Map的欄位存在原本的Table之中。

 

產生的SQL指令

Select

SELECT TOP (1) 
    [Extent1].[Id] AS [Id], 
    [Extent2].[SerialNumber] AS [SerialNumber], 
    [Extent2].[Name] AS [Name], 
    [Extent2].[Gender] AS [Gender], 
    [Extent2].[Photo] AS [Photo], 
    [Extent1].[Address] AS [Address], 
    [Extent1].[Zip] AS [Zip]
    FROM  [dbo].[StudentAddress] AS [Extent1]
    INNER JOIN [dbo].[StudentInformation] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Id]
    WHERE N'Miles' = [Extent2].[Name]

從產生的指令可以看到Entity Framework會自動INNER JOIN StudentInformation。

 

Insert

using (var db = new Model1())
{    
    Student s = new Student()
    {
        Name="Miles",
        Address = "Road 1",
        SerialNumber = 22
    };

    db.Student.Add(s);
    db.SaveChanges();
}
exec sp_executesql N'INSERT [dbo].[StudentInformation]([SerialNumber], [Name], [Gender], [Photo])
VALUES (@0, @1, @2, NULL)
SELECT [Id]
FROM [dbo].[StudentInformation]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()',N'@0 int,@1 nvarchar(max) ,@2 tinyint',@0=22,@1=N'Miles',@2=0

exec sp_executesql N'INSERT [dbo].[StudentAddress]([Id], [Address], [Zip])
VALUES (@0, @1, NULL)
',N'@0 int,@1 nvarchar(max) ',@0=1,@1=N'Road 1'

從產生的指令可以看到,Entity Framework先Insert原本的StudentInformation,Insert完後會將產生的Id再Insert到StudentAddress中,完成兩次Insert的指令。

 

Update

using (var db = new Model1())
{
    var s = db.Student.FirstOrDefault();
    s.Name = "Kevin";
    s.Address = "Avenu";
    db.SaveChanges();
}
exec sp_executesql N'UPDATE [dbo].[StudentInformation]
SET [Name] = @0
WHERE ([Id] = @1)
',N'@0 nvarchar(max) ,@1 int',@0=N'Kevin',@1=1

exec sp_executesql N'UPDATE [dbo].[StudentAddress]
SET [Address] = @0
WHERE ([Id] = @1)
',N'@0 nvarchar(max) ,@1 int',@0=N'Avenu',@1=1

 

如果修改的欄位只會動到一個Table則Entity Framework只會產生一次Update指令,Update該欄位的Table。 如果修改的欄位分散在兩個Table,則Entity Framework會產生兩筆Update指令。

 

Delete

Entity Framework對應這兩個Table的時候是沒有Cascade Delete的。
但是Entity Framework知道刪除的時候需要把兩個Table的資料同時刪除,所以會產生兩筆刪除指令

exec sp_executesql N'DELETE [dbo].[StudentAddress]
WHERE ([Id] = @0)',N'@0 int',@0=1

exec sp_executesql N'DELETE [dbo].[StudentAddress]
WHERE ([Id] = @0)',N'@0 int',@0=1

 

 

小結

Entity Splitting比較會用在已經現有資料庫下,又想將多個Table整理成一個Entity,就可以使用此方式將多個Table合併成一個Entity。

 

 

 

一天一分享,身體好健康。

該追究的不是過去的原因,而是現在的目的。