[ORM] Entity Framework(2) CodeFirst进阶
在上一节中,实现了CodeFirst快速入门。但是很多与数据库的细节还无法自定义。以及使用EF过程中,需要注意的事项。
在本节中,会涉及到以下
- EF中的连接字符串
- EF的对象状态
- 延迟加载,为什么需要Virtual,贪婪加载
- 绕过EF,直接SQL查询
- 查看EF生成SQL语句
- 实体与数据库的映射通过DataAnnotation与FluentAPI两种方式
- 结合MVC异步控制器做个CRUD例子
- 建议
1个完整的连接字符串 IP:端口\实例名 数据库名组成
Nuget安装完EF会在*.config的entityFramework节点下添加,表示使用LocalDb数据库,实例名为v11.0。
1 2 3 4 5 | < defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> < parameters > < parameter value="v11.0" /> </ parameters > </ defaultConnectionFactory > |
而数据库名,默认为类的全限定名。如下调用父类构造函数,表示数据库名为SaleDb。而不是命名空间+类名。
1 2 3 4 5 6 | public class SaleDb : DbContext { public SaleDb(): base ( "SaleDb" ) { } } |
当然,在这个构造函数里,可以完整的写上完整的连接字符串。
db.Entry(TEntity).State (附加Entity,并设置状态)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public enum EntityState { // 摘要: // The entity is not being tracked by the context. An entity is in this state // immediately after it has been created with the new operator or with one of // the System.Data.Entity.DbSet Create methods. Detached = 1, // // 摘要: // The entity is being tracked by the context and exists in the database, and // its property values have not changed from the values in the database. Unchanged = 2, // // 摘要: // The entity is being tracked by the context but does not yet exist in the // database. Added = 4, // // 摘要: // The entity is being tracked by the context and exists in the database, but // has been marked for deletion from the database the next time SaveChanges // is called. Deleted = 8, // // 摘要: // The entity is being tracked by the context and exists in the database, and // some or all of its property values have been modified. Modified = 16, } |
延迟加载(lazy load)是(也称为懒加载),延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。可以简单理解为,只有在使用的时候,才会发出sql语句进行查询。
贪婪加载则是一次性把相关的表也加载进来。
在EF中,默认是支持延迟加载的。
db.Configuration.LazyLoadingEnabled = false; //设置禁用延迟加载
如果想单个贪婪加载某个表。则通过Include("表名")
db.Orders.Include("OrderItems"); //把OrderItems表一并加载进来
Virtual关键字
通常相关连的表作为Entity属性的时候,使用Virtual关键字修饰符。如果不加Virtual关键字,则关联的类只是一个普通的POCO类型。get,set方法为空实现。
使用Virtual关键字后,EF生成一个代理类来重写get和set方法。实现需要的功能。
EF作为一个框架,也不可能做到满足所有需求。EF提供了直接操作Ado.net方式。
有三个 API 支持:
- DbContext.Database.ExecuteSqlCommand
- DbContext.Database.SqlQuery
- DbSet.SqlQuery
执行sql 返回受影响函数。
1 | public int ExecuteSqlCommand( string sql, params object [] parameters); |
执行sql 返回查询结果并自动映射到指定类上。(不会被EF跟踪状态)
1 | DbRawSqlQuery<TElement> SqlQuery<TElement>( string sql, params object [] parameters); |
执行sql 返回查询结果并自动映射到指定类上。(被EF跟踪状态。DbSqlQuery继承自DbRawSqlQuery)
1 | virtual DbSqlQuery<TEntity> SqlQuery( string sql, params object [] parameters); |
当使用EF执行一个复杂的查询时候。我们需要知道EF是否按照我们所需执行。这时,就需要查看EF生成的语句。
- 执行IQueryable<T>.ToString(); 即可查看生成的sql语句。
- 通过EFProviderWrappers,这里不做赘述。
DataAnnotation 特性由.NET 3.5中引进,给.NET中的类提供了一种添加验证的方式。同时在EF中,也是添加约束与个性化设置一种方式。
常用到以下特性。
- KeyAttribute:对应数据库中的主键
- RequiredAttribute:对应数据库中字段的数据是否可以为null
- MaxLengthAttribute:对应数据库中字符串类型字段的最大长度
- MinLengthAttribute:在数据库中无对应,但在代码中字符串最小长度
- TimestampAttribute:将列的数据类型指定为行版本
- DatabaseGeneratedAttribute:标记指定实体属性是由数据库生成的,并指定生成策略(None数据库不生成值,Identity当插入行时,数据库生成值,Computed当插入或更新行时,数据库生成值)
- ColumnAttribute:指定实体属性在数据库中的列名及数据类型
- TableAttribute:指定实体类对应的数据表名
- ForeignKeyAttribute:指定导航属性的外键字段
- NotMappedAttribute:标记指定实体属性在创建数据库中不创建对应字段
- ConcurrencyCheck:并发标记
1 2 3 4 5 6 7 8 9 10 11 | public class Order { [Key] public int Id { get ; set ; } [StringLength(200)] public string Name { get ; set ; } public int UserId { get ; set ; } [ForeignKey( "UserId" )] public User User { get ; set ; } public virtual ICollection<OrderItem> OrderItems { get ; set ; } } |
使用DataAnnotation(DA)非常方便,但有时我们的POCO类不希望受到EF的直接关联。或者DA不能满足需求。这时,我们可以使用FluentAPI方式。
- HasKey - KeyAttribute:配置此实体类型的主键属性
- IsRequired - RequiredAttribute:将此属性配置为必需属性。用于存储此属性的数据库列将不可以为null
- HasMaxLength - MaxLengthAttribute:将属性配置为具有指定的最大长度
- IsConcurrencyToken - ConcurrencyCheckAttribute:将属性配置为用作开放式并发标记
- IsRowVersion - TimestampAttribute:将属性配置为数据库中的行版本。实际数据类型将因使用的数据库提供程序而异。将属性设置为行版本会自动将属性配置为开放式并发标记。
1 2 3 4 | protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<AppInfo>().Property(o => o.Id).HasColumnName( "AppId" ); } |
微软自家的东西关联总是那么方便。
在MVC5中,添加控制器的时候选择使用EF即自动生成这一切。
1.模型改变重新生成数据库,导致表数据丢失。
在使用CodeFirst中,当模型改变的时候,采用第一节中的Nuget中EF Migration API方式则不会清空数据。
2.是否使用存储过程,视图这些数据库技术。
当使用EF 这种ORM框架的时候,就应该轻数据库技术,重业务逻辑层。我的建议是万不得已不要使用存储过程,视图实际只是存储了SQL语句。
MVC5代码下载:MVCTest
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义