深入MVC框架--EF中的导航属性优势与弊端(二)

上一篇中我们解决了因为EF导航属性使用不当出现的问题,其实归根揭底是我们没有配置Model映射关系导致的,即 Fluent API使用EntityTypeConfiguration来配置ORM映射。
Fluent API
实现
总结
利与弊
Fluent API

Fluent API功能非常强大,它在通过重写DbContext上的OnModelCreating方法来实现自定义规则,可以设置主键、组合主键、外键、指定长度、指定属性是否映射到表等等。
创建EntityTypeConfiguration ORM映射。
在OnModelCreating方法下使用,通过反射动态加入。
在上一篇的问题1问题描述,我们也可以通过Fluent API来解决。

实现

创建EntityTypeConfiguration ORM映射。

    public class DepartmentMapper : EntityTypeConfiguration<Department>
    {
        public DepartmentMapper()
        {
            HasKey(t => t.DepartmentID);
            this.Property(t => t.DepartmentName).IsRequired().HasMaxLength(200);
        }
    }
    public class SystemRoleMapper : EntityTypeConfiguration<SystemRole>
    {
        public SystemRoleMapper()
        {
            this.HasKey(t => t.RoleID);
            this.Property(t => t.RoleName).IsRequired().HasMaxLength(200);
            this.Property(t => t.DepartmentID).IsOptional();
            this.Ignore(t => t.DepartmentName);
            this.Ignore(t => t.Functions);
        }
    }
    public class SystemFunctionMapper : EntityTypeConfiguration<SystemFunction>
    {
        public SystemFunctionMapper()
        {
            this.HasKey(t => t.FunctionID);
            this.Property(t => t.FunctionCode).IsRequired().HasMaxLength(50);
            this.Property(t => t.FunctionName).IsRequired().HasMaxLength(200);
        }
    }
    public class SystemRoleFunctionMapper : EntityTypeConfiguration<SystemRoleFunction>
    {
        public SystemRoleFunctionMapper()
        {
            this.HasKey(t => new { t.RoleID, t.FunctionID });
            this.HasRequired(t => t.Function).WithMany().HasForeignKey(t => t.FunctionID);
            this.HasRequired(t => t.Role).WithMany().HasForeignKey(t => t.RoleID);
        }
    }

通过重写DbContext上的OnModelCreating方法来实现自定义规则。
/// <summary>
/// 重载 DbContext.OnModelCreating 方法,添加当前程序集中的所有 EntityTypeConfiguration&lt;&gt;
/// 的Map映射程序实现到DbModelBuilder中。 
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{                        
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
    // dynamically load all configuration
    // System.Type configType = typeof(LanguageMap);
    // any of your configuration classes here
    // var typesToRegister = Assembly.GetAssembly(configType).GetTypes()
    var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
    .Where(type => !String.IsNullOrEmpty(type.Namespace))
    .Where(type => type.BaseType != null && type.BaseType.IsGenericType && 
        type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
    foreach (var type in typesToRegister)
    {
        dynamic configurationInstance = Activator.CreateInstance(type);
        modelBuilder.Configurations.Add(configurationInstance);
    }
    // ...or do it manually below. For example,
    // modelBuilder.Configurations.Add(new LanguageMap());
    base.OnModelCreating(modelBuilder);
}


总结

上述OnModelCreating方法我们通过反射自动添加的Model映射配置(EntityTypeConfiguration类型),在EntityTypeConfiguration配置中我们可以设置如下属性和方法:

  • 设置主键 modelBuilder.Entity<x>().HasKey(t => t.Id);
  • 设置联合主键 modelBuilder.Entity<x>().HasKey(t =>new{t.Code,t.Id} );
  • 取消数据库字段标识(取消自动增长) modelBuilder.Entity<x>().Property(t=>t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
  • 设置数据库字段标识(自动增长) modelBuilder.Entity<SystemOrder>().Property(t =>t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
  • 设置字段最大长度 modelBuilder.Entity<Model_1>().Property(t => t.Name).HasMaxLength(100);
  • 设置字段为必需 modelBuilder.Entity<Model_1>().Property(t =>t.Id).IsRequired();
  • 属性不映射到数据库 modelBuilder.Entity<Model_1>().Ignore(t => t.A);
  • 将属性指定数据库列名: modelBuilder.Entity<Model_1>().Property(t => t.A) .HasColumnName("A_C1");
  • 级联删除课程与学生(数据库默认是不级联删除的) modelBuilder.Entity<Course>().HasRequired(t => t.Student).WithMany(t => t.Student).HasForeignKey(d => d.StudentID).WillCascadeOnDelete();
  • 设置为Timestamp modelBuilder.Entity<Course>() .Property(t => t.Timestamp) .IsRowVersion();
  • 表1对0..1(Course实体可以包含零个或一个Level) modelBuilder.Entity<Level>().HasRequired(t => t.Course).WithOptional(t => t.Level);
  • 表1对1 modelBuilder.Entity<Model_1>().HasRequired(t => t.Model_2).WithRequiredPrincipal(t => tModel_1);
  • 表1对n(Department为主表,一个部门有多个角色) modelBuilder.Entity<SystemRole>().HasRequired(c => c.Department) .WithMany(t => t.SystemRole)
  • 指定外键名(指定表Student中的字段CourseID为外键) modelBuilder.Entity<Course>().HasRequired(c => c.Student).WithMany(t => t.Course).Map(m => m.MapKey("CourseID"));
  • 表n对n,关联表SystemRoleFunction this.HasKey(t => new { t.RoleID, t.FunctionID }); this.HasRequired(t => t.Function).WithMany().HasForeignKey(t => t.FunctionID); this.HasRequired(t => t.Role).WithMany().HasForeignKey(t => t.RoleID);
利与弊

这种方法好处是不言而喻的,除了ORM本身的好处外,我们通过该方法做好了模型与模型、表与表之间的关系,在后续开发直接获取他们的数据就可以,就不用担心他们的关系问题。
但是它有一个明显的缺点,就是我们不能直接去动一丝一毫的数据库表结构,动了就会导致EF数据迁移记录和数据库对不上,导致出错(详细见参考文章 深入Web框架-EF数据迁移常见问题)。
更加让人难以接受的是在删除N对N关系的数据表时,必须用控制台来删除,这个对不能深入理解EF机制的同学来说是致命的,这无疑大大增加了项目数据链路层的复杂性,所以我们决定在 软件开发框架中舍弃了Fluent API这一做法,通过DbSet显示将模型添加到DbContext中。

网站&系统开发技术学习交流群:463167176

本站文章除注明转载外,均为本站原创或翻译,欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,共创和谐网络环境。
转载请注明:文章转载自:华晨软件-云微开发平台 » 深入MVC框架--EF中的导航属性优势与弊端(二)
本文标题:深入MVC框架--EF中的导航属性优势与弊端(二)
本文地址:http://www.hocode.com/OrgTec/DB/0004.html

相关文章: 深入Web框架-EF数据迁移常见问题 | .Net MVC中config怎么配置mysql

电话
电话 18718672256

扫一扫
二维码