讓 EntityFramework 動態支援同樣內容不同名稱的 Table

在我們寫系統 Log 的時候,如果想要搭配使用 Entity Framework 把 Log 寫到資料庫中,會面臨的問題是如果我們想要讓不同的 Log 存放在不同的 Table ,就必須要增加不同的 Class 來對應到不同的 Table ,這樣會造成我們寫 Log 所使用的 DbContext 變得很複雜,卻都是處理同一件事情,還好 Entity Framework 本身就是一個滿有彈性的 Library ,我們可以透過在每次初始化時,事先設定 Log Class 實際對應到的 Table 名稱來做到程式碼共用,那這篇文章主要就是要向大家介紹,透過 Entity Framework ,我們要怎麼讓同一個 Entity Class 可以根據需求支援同樣格式不同名稱的 Table。

使用 Entity Framework 把 Log 寫到資料庫

在開始設定動態轉換之前,我們先建立最基本的 DbContext 和 Log Class 來作為把 Log 寫到資料庫的基礎。

Write Log

  1. 因為我們這次沒有要使用 Code First ,所以先使用 SQL 語法建立資料庫

    USE [DynamicChangeTableName.LogDbContext]
    GO
    
    /****** Object:  Table [dbo].[Logs]    Script Date: 2015/12/31 下午 12:49:43 ******/
    SET ANSI_NULLS ON
    GO
    
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE TABLE [dbo].[Logs](
      [Id] [int] IDENTITY(1,1) NOT NULL,
      [Content] [nvarchar](max) NULL,
      [CreatedAt] [datetime] NOT NULL,
    [CreatedUser] [nvarchar](max) NULL,
    CONSTRAINT [PK_dbo.Logs] PRIMARY KEY CLUSTERED 
    (
      [Id] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
    GO
  2. 建立 Console 專案,並使用 Nuget 安裝 Entity Framework

    Install Entity Framework

  3. 建立 Log Class

    public class Log
    {
      public int Id { get; set; }
    
      public string Content { get; set; }
    
      public DateTime CreatedAt { get; set; }
    
      public string CreatedUser { get; set; }
    }
  4. 建立 LogDbContext

    public class LogDbContext : DbContext
    {
      static LogDbContext()
      {
          Database.SetInitializer<LogDbContext>(null);
      }
    
      public DbSet<Log> Logs { get; set; }
    }
  5. 打開 Program.cs ,撰寫簡單的程式碼看 Entity Framework 的語法是否可以使用

    public class Program
    {
      static void Main(string[] args)
      {
          using (var dbContext = new LogDbContext())
          {
              var log = new Log();
              log.Content = "Test";
              log.CreatedAt = DateTime.Now;
              log.CreatedUser = "TestUser";
    
              dbContext.Logs.Add(log);
              dbContext.SaveChanges();
          }
      }
    }
  6. 執行程式後,我們使用 SSMS 查詢資料庫,可以看到 Log 檔案成功寫入

    Default Log

讓 Log Class 支援不同的 Table 名稱和欄位名稱

如果我們使用 Entity Framework ,要支援讓 Log 寫入不同的 Table ,最直接的作法就是替每一個 Log Table 增加一個 Class ,但是這樣可能會造成很多不必要的程式碼,我們會希望程式碼可以盡可能的重複使用。

Write Log With Entity Framework

Entity Framework 其實支援動態設定 Log Class 與資料庫 Table 的 Mapping ,我們可以透過每次初始化 LogDbContext 時,指定 Table 名稱的方式,來讓 Log Class 可以重複被使用,支援多個不同的 Table。

Write Log With Same Class

接下來就要向大家介紹如何動態設定 Entity Framework 的 Mapping

  1. 新增 LogConfiguration ,設定 Log Class 與 Table 的 Mapping

    internal class LogConfiguration : EntityTypeConfiguration<Log>
    {
      public LogConfiguration()
          : this("dbo", "Log")
      {
      }
    
      public LogConfiguration(string schema, string tableName)
      {
          ToTable(schema + "." + tableName);
    
          HasKey(x => x.Id);
    
          Property(x => x.Id)
              .HasColumnName(tableName + "_Id")
              .IsRequired()
              .HasColumnType("int")
              .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    
          Property(x => x.Content)
              .HasColumnName(tableName + "_Content")
              .IsRequired()
              .IsUnicode(false)
              .HasColumnType("varchar")
              .HasMaxLength(500);
    
          Property(x => x.CreatedAt)
                  .HasColumnName(tableName + "_CreatedAt")
                  .IsRequired()
                  .HasColumnType("datetime");
    
          Property(x => x.CreatedUser)
                  .HasColumnName(tableName + "_CreatedUser")
                  .IsRequired()
                  .IsUnicode(false)
                  .HasColumnType("varchar")
                  .HasMaxLength(50);
        }
    }
  2. 修改 LogDbContext ,在每次初始化的時候指定要使用的 Log Table 名稱

    public class LogDbContext : DbContext
    {
      public string LogTableName { get; private set; }
    
      public DbSet<Log> Logs { get; set; }
    
      static LogDbContext()
      {
          Database.SetInitializer<LogDbContext>(null);
      }
    
      public LogDbContext(string logTableName)
      {                        
          this.LogTableName = logTableName;            
      }
    
      protected override void OnModelCreating(DbModelBuilder modelBuilder)
      {
          base.OnModelCreating(modelBuilder);
    
          modelBuilder.Configurations.Add(new LogConfiguration("dbo", this.LogTableName));
      }
    }
  3. 使用 SQL 新增測試用的 ALog 和 BLog Table

    USE [DynamicChangeTableName.LogDbContext]
    GO
    
    /****** Object:  Table [dbo].[Logs]    Script Date: 2015/12/31 下午 12:47:30 ******/
    SET ANSI_NULLS ON
    GO
    
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE TABLE [dbo].[ALog](
    [ALog_Id] [int] IDENTITY(1,1) NOT NULL,
    [ALog_Content] [nvarchar](max) NULL,
    [ALog_CreatedAt] [datetime] NOT NULL,
    [ALog_CreatedUser] [nvarchar](max) NULL,
    CONSTRAINT [PK_dbo.ALog] PRIMARY KEY CLUSTERED 
    (
      [ALog_Id] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
    CREATE TABLE [dbo].[BLog](
    [BLog_Id] [int] IDENTITY(1,1) NOT NULL,
    [BLog_Content] [nvarchar](max) NULL,
    [BLog_CreatedAt] [datetime] NOT NULL,
    [BLog_CreatedUser] [nvarchar](max) NULL,
    CONSTRAINT [PK_dbo.BLog] PRIMARY KEY CLUSTERED 
    (
      [BLog_Id] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
    GO
  4. 修改 Program.cs 測試是否可以動態切換 Table

    public class Program
    {
      static void Main(string[] args)
      {            
          using (var dbContext = new LogDbContext("ALog"))
          {
              var log = new Log();
              log.Content = "Test";
              log.CreatedAt = DateTime.Now;
              log.CreatedUser = "TestUser";
    
              dbContext.Logs.Add(log);
              dbContext.SaveChanges();
          }
    
          using (var dbContext = new LogDbContext("BLog"))
          {
              var log = new Log();
              log.Content = "Test";
              log.CreatedAt = DateTime.Now;
              log.CreatedUser = "TestUser";
    
              dbContext.Logs.Add(log);
              dbContext.SaveChanges();
          }
      }
    }
  5. 資料有正確寫入

    Result

小結

使用這種方式,就可以讓相同結構不同名稱的 Table 共用同一份 Entity Framework ,讓程式碼可重複使用,也讓資料的管理更為清楚明確,能夠將資料按照不同的需求分類,如果搭配 DI 使用,更甚至可以讓寫入資料的方法更有彈性!關於以上的內容,若有任何問題歡迎大家一起討論喔!