2014年11月26日 星期三

Entity Framework Code First 繼承對應設定

TPH(Table Per Hierarchy)
基底類別和衍生類別都映射到同一張表中,通過一個鑒別欄位來識別資料是屬於基底或是衍生類別,這是預設的映射方法

Table1
namespace ConsoleApplication1.Models
{
    using System.ComponentModel.DataAnnotations;

    public class Table1
    {
        [Key]
        public int Table1Id { get; set; }

        public int C1 { get; set; }

        public int C2 { get; set; }
    }
}

Table2
namespace ConsoleApplication1.Models
{
    public class Table2 : Table1
    {
        public int C3 { get; set; }

        public int C4 { get; set; }
    }
}

新增幾筆資料
namespace ConsoleApplication1
{
    using ConsoleApplication1.Models;

    class Program
    {
        static void Main(string[] args)
        {
            using (DemoContext db = new DemoContext())
            {
                db.Table1.Add(new Table1 { C1 = 1, C2 = 2 });
                db.Table2.Add(new Table2 { C1 = 1, C2 = 2, C3 = 3, C4 = 4 });
                db.SaveChanges();
            }
        }
    }
}

TPH資料表建立的樣子

TPH資料儲存的樣子

可以透過Fluent API設定用來識別的欄位,和識別資料的值
namespace ConsoleApplication1.Models
{
    using System.Data.Entity;

    public class DemoContext : DbContext
    {
        public DbSet<Table1> Table1 { get; set; }
        public DbSet<Table2> Table2 { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Table1>()
                .Map(x=> {
                    x.ToTable("Table1");
                    x.Requires("QQ").HasValue("AA");
                })
                .Map<Table2>(x=> {
                    x.Requires("QQ").HasValue("BB");
                });
        }
    }
}

設定後資料儲存的樣子


TPT(Table Per Type)
把資料映射到各自的資料表中,並在衍生類別的資料表中自動生成外鍵來關聯基厎類別的資料表,使用方式很簡單,只要指定各自資料表的名稱就行了
namespace ConsoleApplication1.Models
{
    using System.Data.Entity;

    public class DemoContext : DbContext
    {
        public DbSet<Table1> Table1 { get; set; }
        public DbSet<Table2> Table2 { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Table1>().ToTable("Table1");
            modelBuilder.Entity<Table2>().ToTable("Table2");
        }
    }
}

TPT資料表建立的樣子

TPT資料儲存的樣子


TPC(Table Per Concreate Type)
除了資料映射到各自的資料表之外,衍生類別的資料表中還會包含基底類別的欄位
透過FluentAPI設定Map即可
namespace ConsoleApplication1.Models
{
    using System.Data.Entity;

    public class DemoContext : DbContext
    {
        public DbSet<Table1> Table1 { get; set; }
        public DbSet<Table2> Table2 { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Table1>()
                .Map(x =>
                {
                    x.ToTable("Table1");
                })
                .Map<Table2>(x =>
                {
                    x.ToTable("Table2");
                    x.MapInheritedProperties();
                });
        }
    }
}

TPC資料表建立的樣子

TPC資料儲存的樣子


2014年11月25日 星期二

Entity Framework Code First Complex Type

Complex Type是用來重覆使用的欄位,有幾個規定
1. 不能有主索引
2. 一個類別中只能有一個實體
3. 只能是引用屬性,不能是集合屬性

Table1
namespace ConsoleApplication1.Models
{
    public class Table1
    {
        public int Table1Id { get; set; }
        public Address Address { get; set; }
    }
}

Table2
namespace ConsoleApplication1.Models
{
    public class Table2
    {
        public int Table2Id { get; set; }
        public Address Address { get; set; }
    }
}

Address
namespace ConsoleApplication1.Models
{
    public class Address
    {
        public string Addr1 { get; set; }

        public string Addr2 { get; set; }
    }
}

預設狀態資料表的樣子

加上DataAnnotation
namespace ConsoleApplication1.Models
{
    using System.ComponentModel.DataAnnotations.Schema;

    [ComplexType]
    public class Address
    {
        [Column("Addr1")]
        public string Addr1 { get; set; }

        [Column("Addr2")]

        public string Addr2 { get; set; }
    }
}

Fluent API設定
namespace ConsoleApplication1.Models
{
    using System.Data.Entity;
    using System.Data.Entity.ModelConfiguration;

    public class DemoContext : DbContext
    {
        public DbSet<Table1> Table1 { get; set; }
        public DbSet<Table2> Table2 { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new AddressConfiguration());
        }

        private class AddressConfiguration : ComplexTypeConfiguration<Address>
        {
            public AddressConfiguration()
            {
                this.Property(x => x.Addr1).HasColumnName("Addr1");
                this.Property(x => x.Addr2).HasColumnName("Addr2");
            }
        }
    }
}

加上設定後資料表的樣子


Entity Framework Code First 多對多關聯設定

兩個類別之間,各自包含對應的集合導覽屬性,就會被當成多對多關系
在資料庫中的多對多關系,需要用三張表來表示,分別是一對多再多對一

一個使用者可以有多個群組
namespace ConsoleApplication1.Models
{
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations.Schema;

    [Table("User")]
    public class User
    {
        public int UserId { get; set; }

        public string UserName { get; set; }

        public virtual ICollection<Group> Group { get; set; }
    }
}

一個群組可以有多個使用者
namespace ConsoleApplication1.Models
{
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations.Schema;

    [Table("Group")]
    public class Group
    {
        public int GroupId { get; set; }

        public string GroupName { get; set; }

        public virtual ICollection<User> User { get; set; }
    }
}

預設會自動生成第三張表


透過FluentAPI設定第三張表的名稱,和欄位名稱
namespace ConsoleApplication1.Models
{
    using System.Data.Entity;

    public class DemoContext : DbContext
    {
        public DbSet<User> User { get; set; }

        public DbSet<Group> Group { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>()
                .HasMany(x => x.Group)
                .WithMany(x => x.User)
                .Map(x =>{
                    x.ToTable("MyUserGroup");
                    x.MapLeftKey("UserID");
                    x.MapRightKey("GroupID");
                });
        }
    }
}

資料表建立的樣子

關聯圖

Entity Framework Code First 一對多關聯設定

兩個類別之間,一個包含集合導覽屬性,一個包含引用屬性,就會被當成一對多關系

主表
namespace ConsoleApplication1.Models
{
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations.Schema;

    [Table("Topic")]
    public class Topic
    {
        public int TopicId { get; set; }

        public string Title { get; set; }

        public virtual ICollection<Reply> Reply { get; set; }
    }
}

明細表
外鍵的命名只有符合預設的規則,就會建立關系,規則如下
1. 目標類型索引的名稱
2. 目標類型名稱+目標類型主索引名稱
3. 導覽屬性名稱+目標類型主索引名稱
namespace ConsoleApplication1.Models
{
    using System.ComponentModel.DataAnnotations.Schema;

    [Table("Reply")]
    public class Reply
    {
        public int ReplyId { get; set; }

        public int TopicId { get; set; }
        // public int TopicTopicId { get; set; }
        // public int TheTopicTopicId { get; set; }

        public string Title { get; set; }
        
        public virtual Topic TheTopic { get; set; }
    }
}

若不是用預設規則的命名方式,可以透過ForeignKey這個Attribute設定
namespace ConsoleApplication1.Models
{
    using System.ComponentModel.DataAnnotations.Schema;

    [Table("Reply")]
    public class Reply
    {
        public int ReplyId { get; set; }

        [ForeignKey("TheTopic")]
        public int tid { get; set; }

        public string Title { get; set; }
        
        public virtual Topic TheTopic { get; set; }
    }
}

也可以設定在引用屬性上
namespace ConsoleApplication1.Models
{
    using System.ComponentModel.DataAnnotations.Schema;

    [Table("Reply")]
    public class Reply
    {
        public int ReplyId { get; set; }

        public int tid { get; set; }

        public string Title { get; set; }

        [ForeignKey("tid")]
        public virtual Topic TheTopic { get; set; }
    }
}

透過FluentAPI設定方式
namespace ConsoleApplication1.Models
{
    using System.Data.Entity;

    public class DemoContext : DbContext
    {
        public DbSet<Topic> Topic { get; set; }

        public DbSet<Reply> Reply { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Topic>()
                .HasMany(x => x.Reply)
                .WithRequired(x => x.TheTopic)
                .HasForeignKey(x => x.tid)
                .WillCascadeOnDelete(true);
        }
    }
}

資料表建立的樣子

關聯圖



2014年11月24日 星期一

Entity Framework Code First 一對一關聯設定


在兩個類別之間,各自包含一個引用屬性,就會被當成一對一關系
只是那張是主資料表,需要利用Fluent API來設定

主資料表
using System.ComponentModel.DataAnnotations;

public partial class TableA
{
    [Key]
    public int TabId { get; set; }

    public int C1 { get; set; }

    public virtual TableB TableB { get; set; }
}

對應的資料表
using System.ComponentModel.DataAnnotations;

public partial class TableB
{
    [Key]
    public int TabId { get; set; }

    public int CC1 { get; set; }

    public virtual TableA TableA { get; set; }
}

設定關聯
using System.Data.Entity;

public partial class DemoContext : DbContext
{
    public DemoContext()
        : base("name=DemoContext")
    {
    }

    public virtual DbSet<TableA> TableA { get; set; }
    public virtual DbSet<TableB> TableB { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<TableA>()
            .HasOptional(x => x.TableB)
            .WithRequired(x => x.TableA);
    }
}

建立出一對一關系的資料表


關聯圖