EF Core 實體映射表或視圖


===============================================

 2020/8/12_第3次修改                       ccb_warlock

 

更新說明:

2020/8/12:

1.增加了參考資料的內容

2020/6/8:

1.增加了在mysql上試驗的結果說明;

===============================================

最近忙於公司的業務系統,終於有時間對框架結構進行完善。在開發子系統時需要連接的是oracle中帶用戶名前綴的數據表(因為客戶提供的是其他的用戶),然而之前的框架實現中沒有對這方面的內容做考慮(因為框架中EF Core實際只使用了MSSQL,而且業務只操作了當前庫里的表(即dbo)),於是針對EF Core的實體映射還是單獨寫篇文章做個記錄。

 

2020/6/8,我在MySQL上也進行了試驗,這種實體映射方式也是支持的。

 

我在開發中采用的是Code First,所以這里不去深究記錄的內容是否在DB First中可以應用。

 


一、當前庫/當前用戶下,實體與表的映射

在實現的過程中,有2種映射的處理(這里以用戶實體舉例):

1)通過特性標記實體映射的表,再到DbContext中根據程序集的添加實體到模型中

// 實體定義並定義表映射

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Common.Entities;

namespace Entity.Po
{
    [Table("USER_T")]
    public class User : BaseEntity
    {
        [Column("NAME")]
        [DataType("varchar")]
        [MaxLength(30)]
        public string Name { get; set; }
        
        [Column("PASSWORD")]
        [DataType("varchar")]
        [MaxLength(50)]
        public string Password { get; set; }
    }
}

 

// 在模型中添加實體

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Core.Dao
{
    public class CommonDbContext : DbContext
    {
        private static Assembly _asmEntity;

        private static DbConfig _dbConfig;//這個是自定義的數據庫配置,通過依賴注入獲取

        private CommonDbContext(DbContextOptions options) : base(options)
        {

        }

        // todo:創建上下文等等
        
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            LoadModelBuilder(modelBuilder);
            base.OnModelCreating(modelBuilder);
        }

        private static void LoadModelBuilder(ModelBuilder modelBuilder)
        {
            if (null == _dbConfig) throw new Exception("DB Configuration Not Found.");
            
            _asmEntity = Assembly.Load("Entity");
            if (null == _asmEntity) throw new Exception("Entity Assembly Not Found.");

            var method = modelBuilder.GetType().GetMethod(
                "Entity",
                BindingFlags.Instance | BindingFlags.Public,
                null,
                new[] { typeof(Type) }, null
            );

            if (null == method) return;

            foreach (var type in _asmEntity.ExportedTypes)
            {
                //設計在數據庫配置信息中,增加了PO的基類標記,當該實體繼承該基類,表示該實體是個PO
                if (!type.IsSubclassOf(_dbConfig.BaseEntityClass)) continue;

                modelBuilder.Entity(type);
            }
        }
    }
}

 

2)在DbContext中定義每個實體的DbSet,再在模型中添加實體並定義表映射

// 實體定義

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Common.Entities;

namespace Entity.Po
{
    public class User : BaseEntity
    {
        public string Name { get; set; }
        
        public string Password { get; set; }
    }
}

 

// 在模型中添加實體並定義表映射

using Microsoft.EntityFrameworkCore;

namespace Core.Dao
{
    public class CommonDbContext : DbContext
    {
        public CommonDbContext(DbContextOptions<CommonDbContext> options) : base(options)
        {
        
        }

        // todo:創建上下文等等
        
        public DbSet<User> User { get; set; }
        
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>(entity =>
            {
                entity.ToTable("USER_T");

                entity.Property(e => e.Id)
                    .HasColumnName("ID")
                    .HasColumnType("varchar")
                    .HasMaxLength(36);
                
                entity.Property(e => e.Dname)
                    .HasColumnName("NAME")
                    .HasColumnType("varchar")
                    .HasMaxLength(30);
                
                entity.Property(e => e.Password)
                    .HasColumnName("PASSWORD")
                    .HasColumnType("varchar")
                    .HasMaxLength(50);
            });
        }
    }
}

 


二、非當前庫/非當前用戶下,實體與表的映射(帶前綴)

在MSSQL中,當連接數據庫ABC需要訪問數據庫DEF時,需要增加數據庫前綴,如下:

SELECT * FROM DEF.USER_T;

 

在Oracle中,當用戶A需要訪問用戶B的表時,需要增加用戶前綴,如下:

SELECT * FROM B.USER_T;

 

直接在“一、當前庫/當前用戶下,實體與表的映射”的表名前增加前綴是無效的,運行后會提示表或視圖不存在。

查看官方文檔(https://docs.microsoft.com/en-us/ef/core/modeling/entity-types?tabs=data-annotations)后,發現原來已經提供了一種統一的屬性“表模式”來實現該前綴的標記。

 

在實現的過程中,有2種映射的處理(這里以用戶實體舉例):

1)通過特性標記實體映射的表,再到DbContext中根據程序集的添加實體到模型中

// 實體定義並定義表映射(例如在MSSQL中,當連接數據庫ABC需要訪問數據庫DEF時)

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Common.Entities;

namespace Entity.Po
{
    [Table("USER_T", Schema = "DEF")]
    public class User : BaseEntity
    {
        [Column("NAME")]
        [DataType("varchar")]
        [MaxLength(30)]
        public string Name { get; set; }
        
        [Column("PASSWORD")]
        [DataType("varchar")]
        [MaxLength(50)]
        public string Password { get; set; }
    }
}

 

// 在模型中添加實體

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Core.Dao
{
    public class CommonDbContext : DbContext
    {
        private static Assembly _asmEntity;

        private static DbConfig _dbConfig;//這個是自定義的數據庫配置,通過依賴注入獲取

        private CommonDbContext(DbContextOptions options) : base(options)
        {

        }

        // todo:創建上下文等等
        
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            LoadModelBuilder(modelBuilder);
            base.OnModelCreating(modelBuilder);
        }

        private static void LoadModelBuilder(ModelBuilder modelBuilder)
        {
            if (null == _dbConfig) throw new Exception("DB Configuration Not Found.");
            
            _asmEntity = Assembly.Load("Entity");
            if (null == _asmEntity) throw new Exception("Entity Assembly Not Found.");

            var method = modelBuilder.GetType().GetMethod(
                "Entity",
                BindingFlags.Instance | BindingFlags.Public,
                null,
                new[] { typeof(Type) }, null
            );

            if (null == method) return;

            foreach (var type in _asmEntity.ExportedTypes)
            {
                //設計在數據庫配置信息中,增加了PO的基類標記,當該實體繼承該基類,表示該實體是個PO
                if (!type.IsSubclassOf(_dbConfig.BaseEntityClass)) continue;

                modelBuilder.Entity(type);
            }
        }
    }
}

 

2)在DbContext中定義每個實體的DbSet,再在模型中添加實體並定義表映射

// 實體定義

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Common.Entities;

namespace Entity.Po
{
    public class User : BaseEntity
    {
        public string Name { get; set; }
        
        public string Password { get; set; }
    }
}

 

// 在模型中添加實體並定義表映射

using Microsoft.EntityFrameworkCore;

namespace Core.Dao
{
    public class CommonDbContext : DbContext
    {
        public CommonDbContext(DbContextOptions<CommonDbContext> options) : base(options)
        {
        
        }

        // todo:創建上下文等等
        
        public DbSet<User> User { get; set; }
        
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.HasDefaultSchema("DEF");

            modelBuilder.Entity<User>(entity =>
            {
                entity.ToTable("USER_T");

                entity.Property(e => e.Id)
                    .HasColumnName("ID")
                    .HasColumnType("varchar")
                    .HasMaxLength(36);
                
                entity.Property(e => e.Dname)
                    .HasColumnName("NAME")
                    .HasColumnType("varchar")
                    .HasMaxLength(30);
                
                entity.Property(e => e.Password)
                    .HasColumnName("PASSWORD")
                    .HasColumnType("varchar")
                    .HasMaxLength(50);
            });
        }
    }
}

using Microsoft.EntityFrameworkCore;

namespace Core.Dao
{
    public class CommonDbContext : DbContext
    {
        public CommonDbContext(DbContextOptions<CommonDbContext> options) : base(options)
        {
        
        }

        // todo:創建上下文等等
        
        public DbSet<User> User { get; set; }
        
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>(entity =>
            {
                entity.ToTable("USER_T", "DEF");

                entity.Property(e => e.Id)
                    .HasColumnName("ID")
                    .HasColumnType("varchar")
                    .HasMaxLength(36);
                
                entity.Property(e => e.Dname)
                    .HasColumnName("NAME")
                    .HasColumnType("varchar")
                    .HasMaxLength(30);
                
                entity.Property(e => e.Password)
                    .HasColumnName("PASSWORD")
                    .HasColumnType("varchar")
                    .HasMaxLength(50);
            });
        }
    }
}

 


總結:

從框架設計解耦與代碼的維護角度,我更推薦“使用特性標記實體映射的表”的方式。

 

因為映射的特性將會在實體定義時一起維護,這樣每次增刪改實體時就不需要修改DbContext的源碼,獨立了該實體添加的功能。

 


參考資料:

1.https://docs.microsoft.com/en-us/ef/core/modeling/entity-types?tabs=data-annotations

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM