约定,类似于接口,是一个规范和规则,使用Code First 定义约定来配置模型和规则。在这里约定只是记本规则,我们可以通过Data Annotaion或者Fluent API来进一步配置模型。约定的形式有如下几种:

  • 类型发现约定
  • 主键约定
  • 关系约定
  • 复杂类型约定
  • 自定义约定

零、类型发现约定

在Code First 中。我们定义完模型,还需要让EF上下文你知道应该映射那些模型,此时我们需要通过 DbSet 属性来暴露模型的。如果我们定义的模型由继承层次,只需要为基类定义一个DbSet属性即可(如果派生类与基类在同一个程序集,派生类将会被自动包含),代码如下:

public class Department
{public int DepartmentId { get; set; }public string Name { get; set; }public virtual ICollection<Blog> Blogs { get; set; }
}public class EfDbContext : DbContext
{public EfDbContext(){}public DbSet<Department> Departments { get; set; }
}复制代码

当然,有时候我们不希望模型映射到数据库中,这时我们可以通过Fluent API 来忽略指定的模型映射到数据库中,代码写在EF上下文中:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{modelBuilder.Ignore<Department>();
}
复制代码

一、主键约定

Code First 会根据模型中定义的***id***,推断属性为主键(如果类中没有id属性,会查找定义成***类名称+id***的属性,将这个属性作为主键)。如果主键类型是***int*** 或者 guid 类型,主键将会被映射为自增长标识列。例如我们上一小节中定义的类 Department,类中没有名称为id的属性,但是存在名称为类名称+id的属性***DepartmentId***,因此DepartmentId属性,将会被映射为自增长的主键。如果一个类中既没有id属性,也没有类名+id的属性,那么代码在运行时将会报错,因为EF没有找到符合要求的字段创建主键。

二、关系约定

在数据库中,我们可以通过多张表的关联查询出数据,这多张表之间的关联,就是他们的关系。同样,也可以在模型中定义这样的关系。EF中定义关系要使用到导航属性,通过导航属性可以定义多个模型之间的关系。大部分情况下我们会将导航属性和外键属性结合在一起使用。导航属性的命名规则如下:导航属性名称+主体主键名称 或者 主体类名+主键属性名称 或者 主体主键属性名。当EF检测出外键属性后,会根据外键属性是否为空来判断关系,如果外键可以为空,那么模型之间的关系将会配置成可选的,Code First 不会再关系上配置级联删除。看一个简单的代码:

public class Department
{public int DepartmentId { get; set; }public string Name { get; set; }public virtual ICollection<Student> Students { get; set; }
}public class Student
{public int StudentId { get; set; }public string Name { get; set; }public int DepartmentId { get; set; }public virtual Department Department { get; set; }
}
复制代码

三、复杂类型约定

在Code First 不能推断出模型中的主键,并且没有通过Data Annotations 或者Fluent API进行手动配置主键时,该模型将会自动被配置为复杂类型,检测复杂类型时要求该类型没有引用实体类型的属性。简单的说就是:一个复杂类型作为已存在对象的属性,EF会将复杂类型的类映射到已存在的表中,已存在的表包将包含这些列,而不是将复杂类型映射成另外单独的一张表。我们来看一下例子:

public class EfDbContext : DbContext
{protected override void OnModelCreating(DbModelBuilder modelBuilder){modelBuilder.Entity<Order>().ToTable("Orders");modelBuilder.ComplexType<Order.Address>();}public DbSet<Order> Orders { get; set; }
}public class Order
{public int Id;public string Name;public class Address{public string Street;public string Region;public string Country;}
}复制代码

四、自定义约定

当EF提供的默认约定都不符合我们要求的时候,我们可以使用自定义约定。自定义约定可以看作全局约定规则,将会运用到所有实体和属性,也可以显示实现应用到指定的模型上。

如果项目要求模型中有Id属性,就将Id作为主键映射,那么我们有两种选择来定义这个约定,首先我们而已选择Fluent API ,其次我们也可以选择自定义约定。自定义约定相对来说比Fluent API 要简单,只需一行代码即可解决。我们只需要在 OnModelCreating 方法中加入如下代码即可:

modelBuilder.Properties().Where(p => p.Name == "Id").Configure(p => p.IsKey());
复制代码

注:当多个属性存在相同约定配置时,最后一个约定将覆盖前面所有相同的约定。

自定义约定包含一个约定接口 IConvention,IConceptualModelConvention 是概念模型接口,在模型创建后被调用,IStoreModelConvention 接口为存储模型接口,在模型创建之后用于操作对模型的存储,***自定义类约定***都必须在 OnModelCreating 方法中显式配置,例如我们要将模型中类型为DateTime的属性映射为datetime2,可进行如下配置:

public class DateTime2Convention : Convention
{public DateTime2Convention(){this.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));}
}protected override void OnModelCreating(DbModelBuilder modelBuilder)
{modelBuilder.Conventions.Add(new DateTime2Convention());
}复制代码

当我们自定义约定需要在另一个约定运行之前或者运行之后执行时,有可能会受到默认原定的影响,这时我们可以用到:*AddBeforeAddAfter 方法,例如:将我们前面创建的约定放在内置约定发现逐渐约定之前运行。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{modelBuilder.Conventions.AddBefore<IdKeyDiscoveryConvention>(new DateTime2Convention());
}
复制代码

在开发过程中都会存在开发规范,例如对表名命名的规则,我们可以调用Types 方法该表表明约定,代码如下:

public string GetTableName(Type type)
{var result = Regex.Replace(type.Name, ".[A-Z]",m=>m.Value[0]+"_"+m.Value[1]);return result.ToLower();
}protected override void OnModelCreating(DbModelBuilder modelBuilder)
{modelBuilder.Types().Configure(c => c.ToTable(GetTableName(c.ClrType)));
}
复制代码

上述我们讲的都是针对全局的约定,我们在开发工程中大部分遇到的是针对符合特定条件的模型进行约定,此时我们就用到了自定义特性。我们先来看一段代码:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class NoUnicode : Attribute
{
}
复制代码

这段代码将类型为字符串的属性配置为非Unicode,下面我们建上面的特性应用到所有模型

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{modelBuilder.Properties().Where(x => x.GetCustomAttributes(false).OfType<NoUnicode>().Any()).Configure(c => c.IsUnicode(false));
}
复制代码

添加该特性后,映射在数据库中的列将是 varchar 类型,而不是 nvarchar 类型。但是上述代码存在一个问题,如果匹配的不是字符串类型将会报错,因此我们将代码更新如下:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{modelBuilder.Properties().Where(c => c.GetCustomAttributes(false).OfType<NoUnicode>().Any()).Configure(c => c.IsUnicode(false));modelBuilder.Properties().Having(x => x.GetCustomAttributes(false).OfType<IsUnicode>().FirstOrDefault()).Configure((config, attr) => config.IsUnicode(attr.Uniconde));
}[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class NoUnicode : Attribute
{
}[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
internal class IsUnicode : Attribute
{public bool Uniconde { get; set; }public IsUnicode(bool isUnicode){Uniconde = isUnicode;}
}
复制代码

转载于:https://juejin.im/post/5d01b808f265da1bcd37d1cc

Entity Framework 约定相关推荐

  1. Entity Framework Code First关系映射约定

    本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...

  2. Entity Framework 6新特性:全局性地自定义Code First约定

    2012年12月11日,Entity Framework已经发布了Entity Framework 6 Alpha2,因项目需要,目前已使用了其中的两个特性,今天就来介绍一下第一个特性:全局性地自定义 ...

  3. 【EF】Entity Framework 6新特性:全局性地自定义Code First约定

    应用场景 场景一:EF Code First默认使用类名作为表名,如果我们需要给表名加个前缀,例如将类名Category映射到表Shop_Category.将Product映射到Shop_Produc ...

  4. ABP 基础设施层——集成 Entity Framework

    本文翻译自ABP的官方教程<EntityFramework Integration>,地址为:http://aspnetboilerplate.com/Pages/Documents/En ...

  5. Entity Framework 5.0基础系列

    1.Entity Framework简介 http://www.cnblogs.com/aehyok/p/3315991.html 2.Entity Framework DBFirst尝试http:/ ...

  6. Code First :使用Entity. Framework编程(7) ----转发 收藏

    第7章 高级概念 The Code First modeling functionality that you have seen so far should be enough to get you ...

  7. sql注入pythonpoco_.NET EF(Entity Framework)详解

    一丶Entity Framework (一)EF简介 (1)ORM:Object Relation Mapping ,通俗说:用操作对象的方式来操作数据库. (2)插入数据库不再是执行Insert,而 ...

  8. 《你必须掌握的Entity Framework 6.x与Core 2.0》书籍出版

    前言 到目前为止写过刚好两百来篇博客,看过我博客的读者应该大概知道我每一篇博客都沿袭着一贯的套路,从前言到话题最终到总结,本文依然是一如既往的套路,但是不是介绍技术,也可说是介绍技术,不过是介绍书中的 ...

  9. 浅析Entity Framework Core中的并发处理

    前言 Entity Framework Core 2.0更新也已经有一段时间了,园子里也有不少的文章.. 本文主要是浅析一下Entity Framework Core的并发处理方式. 1.常见的并发处 ...

最新文章

  1. JAVA中的Font
  2. SPRING STS Virgo OSGI 开发一 : bundle 项目的创建
  3. JAVA程序设计----集合基础之Collection
  4. 常见分数值归一化方法
  5. 关于求已知整数数组的连续子数组的最大和的方法 ——基于软件工程的要求给予优化...
  6. 技术创造新商业:云研发时代的效能挑战 | 凌云时刻
  7. Keil 5安装教程
  8. G.Power教程 | 样本量估计
  9. HDOJ 4944 FSF’s game
  10. Readyboost技术U 盘变内存,加速电脑(只针对Windows 7和Vista系统)
  11. 理论小知识:集合之scard
  12. 【渝粤题库】陕西师范大学292251 公司金融学Ⅰ 作业(高起专)
  13. C# Socket网络编程精华篇 (转)
  14. 叫你如何查看QQ空间秘密发布者的发布时间
  15. h5调用Android 的方法
  16. 银河麒麟桌面版系统-用户密码到期无法正常进入系统解决办法
  17. FPGA 之 SOPC 系列(九)SOPC 补充:altera与xilinx对比
  18. tf.roll:tensorflow 中对多维Tensor移位
  19. Install DBeaver in Mac
  20. 天猫商城如何创建店铺宝营销活动?

热门文章

  1. [高级]android应用开发之intent的妙用二
  2. python数据结构与算法:队列与双端队列
  3. Java12和Jdk12安装以及OpenJdk12源码
  4. Linux系统轻量级监控工具monitorix和munin安装
  5. 基于第四层交换技术的负载均衡
  6. ASP.net实现无扩展名的URL重写。简单、方便、无需ISAPI
  7. Linux上隐藏进程名(初级版)
  8. LIVE555简介及在Windows上通过VS2013编译操作步骤
  9. 【C】printf warning: unknown conversion type character ‘l‘ in format [-Wformat=]
  10. 【C++】Google C++编码规范(二):类