[Entity Framework][Code First]Allowing Multiple Entities to Map to a Single Table

Table Splitting

前言

資料庫的欄位只要一多,未經調整的情況下使用Entity Framework將資料撈回來,會把全部的欄位都撈回來,可是實際上某些情況,你常常用的到欄位就只有一些而已,並不是全部都會用到。  這種多拉回來的資料也是一種浪費,而Entity Framework 可以使用Table Splitting的方式來解決這個問題,Table Splitting允許兩個POCO class對應到資料庫的一個table,而取資料的時候只會取得一個POCO class的內容。

 

我們Person Table為例,假設我們實際上比較會用到Name這個欄位,而Image,Caption比較少用到。

而且做的則是將Person拆成兩個POCO class,Perosn與PersonPhoto,且使用Data Annotation指向同一個table name ,如下:

[Table("Person")]
public class Person
{
    [Key]
    public int PersonId { get; set; }

    public string Name { get; set; }
    
    public PersonPhoto Photo { get; set; }
}

[Table("Person")]
public class PersonPhoto
{
    [Key]        
    public int PersonId { get; set; }

    public byte[] Image { get; set; }

    public string Caption { get; set; }
    
    public Person Person { get; set; }
}

接著使用Fluent API設定關聯

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .HasRequired(p => p.Photo)
        .WithRequiredDependent(p => p.Person);
}

這樣就完成兩個POCO class對應到一個table的方式了。

 

注意,使用Table Splitting規則:

POCO class 必須要one-to-one relationshop

POCO class 必須共享同一個Key

 

產生的SQL

接下來我們來看,Entity Framework怎麼幫我們產生SQL

SELECT

var person = db.Person.FirstOrDefault(x => x.Name.Equals("Miles"));
SELECT TOP (1) 
    [Extent1].[PersonId] AS [PersonId], 
    [Extent1].[Name] AS [Name]
    FROM [dbo].[Person] AS [Extent1]
    WHERE N'Miles' = [Extent1].[Name]

使用Eager loading

var person = db.Person.Include("Photo").FirstOrDefault(x => x.Name.Equals("Miles"));
SELECT TOP (1) 
    [Extent1].[PersonId] AS [PersonId], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[Image] AS [Image], 
    [Extent1].[Caption] AS [Caption]
    FROM [dbo].[Person] AS [Extent1]
    WHERE N'Miles' = [Extent1].[Name]

 

INSERT

using (var db = new Model1())
{
    var p = new Person()
    {
        Name = "Bob",
        Photo = new PersonPhoto()
    };

    db.Person.Add(p);
    db.SaveChanges();
}
exec sp_executesql N'INSERT [dbo].[Person]([Name], [Image], [Caption])
VALUES (@0, NULL, NULL)
SELECT [PersonId]
FROM [dbo].[Person]
WHERE @@ROWCOUNT > 0 AND [PersonId] = scope_identity()',N'@0 nvarchar(max) ',@0=N'Bob'

 

UPDATE

using (var db = new Model1())
{
    var person = db.Person.FirstOrDefault(x => x.Name.Equals("Bob"));
    person.Name = "Kevin";
    db.SaveChanges();
}
exec sp_executesql N'UPDATE [dbo].[Person]
SET [Name] = @0
WHERE ([PersonId] = @1)
',N'@0 nvarchar(max) ,@1 int',@0=N'Kevin',@1=2

 

DELETE

方法一:
using (var db = new Model1())
{
    var person = db.Person.Include("Photo").SingleOrDefault(x => x.Name.Equals("Miles"));    
    db.PersonPhoto.Remove(person.Photo);
    db.Person.Remove(person);
    db.SaveChanges();
}
方法二:
using (var db = new Model1())
{
    var person = db.Person.Include("Photo").SingleOrDefault(x => x.Name.Equals("Miles"));
    db.Entry<PersonPhoto>(person.Photo).State = EntityState.Deleted;
    db.Entry<Person>(person).State = EntityState.Deleted;    
    db.SaveChanges();
}

 

exec sp_executesql N'DELETE [dbo].[Person]
WHERE ([PersonId] = @0)',N'@0 int',@0=2

 

小結

Table Splitting主要是幫助我們加快搜尋的速度,實際上Delete,Update,Insert幫助沒有太大,反而要多寫code,所以至於到底要不要使用Table Splitting還是得視情況而定。

 

 

 

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

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