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

 

 

原文地址:https://www.cnblogs.com/straycats/p/12990558.html