项目Contoso 大学
1.验证提示信息:
View:
<%=Html.ValidationSummary("") %>
Controller:
ModelState.AddModelError("Error", "您提交的信息还未通过审核!"); if ((!this.ModelState.IsValid)) return this.RedirectWithErrors(this.View(DeleteViewName, viewModel), viewModel.GetErrors());
2.确认数据库连接没有忘记关闭:
protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); }
3.EF 延迟,饿汉,以及显式加载关联数据
EF 有多种方式可以通过导航属性加载关联的数据。
- 延迟加载 Lazy Loading。当实体第一次读取的时候,关联的数据并不会被获取。 实际上,当第一次你实际访问关联属性的时候,被导航属性关联的数据才会被自动的读取。 这可能导致多次查询被发送到数据库 – 一次是读取实体本身, 对于关联的每个实体也需要分别读取。
- 饿汉加载 Eager Loaing。当实体加载的时候,相关联的数据也一起被加载。典型地用在一次连接查询返回所有需要的相关数据,通过使用 Include 方法实现饿汉加载。
- 显式加载 Explict Loading。这种方式类似于延迟加载,除了需要在代码中显式获取数据。在你访问导航属性的时候,不会出现自动加载。你自己手动加载关联的数据,通过访问对象状态管理器来获取实体,调用 Collection.Load 方法获取集合,或者通过调用持有单个实体的属性的 Reference.Load 方法。( 在下面的示例中,如果你希望加载 Administrator 导航属性,你应该将 Collection( x=>x.Course ) 替换为 Reference( x=>x.Administrator ) 。
因为不会立即获取关联属性的值,延迟加载和显式加载又被称为延后加载。
一般来说,如果你知道你需要每个实体的关联属性,饿汉加载提供了最好的性能。因为只有一次查询被发送到数据库,比对每个实体都要向数据库发出一次查询要更加有效。例如,在上面的例子中,假设每个系都有相关的课程,饿汉加载只需要一次联合查询就可以获得。而使用延迟加载或者显式加载则需要 11 次查询。
从另外的角度来说,如果你不常访问实体的导航属性,或者仅仅访问一小部分实体的导航属性,延迟加载更加有效,因为饿汉加载会加载更多地不必要的数据。通常情况下,在关闭了延迟加载的情况下使用显式加载。一个关闭延迟加载的场景是在进行序列化的时候,当你知道不需要所有的导航属性数据加载。如果延迟加载启用,所有的导航属性将会自动加载,因为序列化会访问所有的属性。
数据库上下文默认支持延迟加载,有两种方法可以关闭延迟加载:
- 对于特定的导航属性,在定义属性的时候取消 virtual
- 对于所有的导航属性,设置 LazyLoadingEnabled 为假。
延迟加载可能导致性能问题,例如,代码中没有指定使用饿汉加载或者显式加载,但是在处理大量实体的时候,遍历每个实体并访问其导航属性可能导致低效率 ( 因为多次访问数据库 ), 但是使用延迟加载不会出现问题。在代码使用延迟加载的时候临时禁用延迟加载可能导致出现问题。因为导航属性为 null 而导致代码访问对象失败。
4.@Html.DisplayFor(modelItem => item.Grade) : DisplayFor 方法住手方法用来是的在成绩为 null 的时候显示 “No grade”,如在这个属性的 DisplayFormat 特性中定义的那样。
7-1 并发冲突
(原文:http://www.cnblogs.com/haogj/archive/2012/04/22/2465613.html)
并发冲突出现在这样的时候,一个用户正在显示并编辑一个实体,但是在这个用户将修改保存到数据库之前,另外的一个用户却更新了同样的实体。如果你没有通过 EF 检测类似的冲突,最后一个更新数据的用户将会覆盖其他用户的修改。在一些程序中,这样的风险是可以接受的,如果只有很少的用户,或者很少的更新,甚至对数据的覆盖不是真的很关键,或者解决并发的代价超过了支持并发所带来的优势。在这种情况下,你就不需要让你的程序支持并发冲突的处理。
7-1-1 悲观并发 ( 锁定 )
如果你的应用需要在并发环境下防止偶然的数据丢失,一种方式是通过数据库的锁来实现。这种方式被称为悲观并发。例如,在从数据库中读取一行数据之前,可以申请一个只读锁,或者一个更新访问锁。如果你对数据行使用了更新访问锁,就没有其他的用户可以获取不管是只读锁还是更新访问锁,因为他们可能获取正在被修改中的数据。如果你使用只读锁来锁定一行,其他用户也可以使用只读访问,但是不能进行更新。
管理锁有一些缺点,对程序来说可能很复杂。它需要重要的数据库管理资源,对于大量用户的时候可能导致性能问题 ( 扩展性不好 ),由于这些原因,不是所有的数据库管理系统都支持悲观锁。EF 对悲观锁没有提供内建的支持,这个教程也不会演示如何实现它。
7-1-2 乐观并发
除了悲观并发之外的另一方案是乐观并发。乐观并发意味着允许并发冲突发生,如果出现了就做出适当的反应。
为了告诉 EF 在实体中有一个属性表示并发标识,你可以通过标签 [ConcurrencyCheck] 来标识这个属性,或者使用模型构建器。我认为并发标识定义了业务规则,应该是模型的一部分。所以这里使用标签。
public class Order { public int OrderID { get; set; } [Required] [StringLength(32, MinimumLength = 2)] public string OrderTitle { get; set; } [Required] [StringLength(64, MinimumLength=5)] public string CustomerName { get; set; } public DateTime TransactionDate { get; set; } [ConcurrencyCheck] [Timestamp] public byte[] TimeStamp { get; set; } public virtual List<OrderDetail> OrderDetails { get; set; } public virtual List<Employee> InvolvedEmployees { get; set; } }
在这段代码中,当我们通过 DbContext 调用 SaveChanges 的时候,将会使用乐观并发。
Timestamp 属性的类型是 byte[], 通过标签 Timestamp ,将这个属性映射到 SQL Server 的 time-stamp 类型的列。
8.单表继承
表的设计:
类的设计:
创建 Person 类,将 Instructor类 和 Student 类从 Person 类中派生
9-4 实现泛型的仓储和工作单元
对每一个实体类型创建一个仓储将会导致大量重复代码。还会带来部分更新的问题。例如,假设在一个事务中更新两个不同的实体。 如果每一个仓储使用不同的数据库上下文实例,一个可能成功了,另外一个失败了。一种减少冗余代码的方式是使用泛型仓储,另一种方式是使用工作单元类来确保所有的仓储都使用同样的数据库上下文 ( 来协调所有的更新 )。
9-4-2 创建工作单元类
工作单元类服务于一个目的:当你使用多个仓储的时候,共享单个的数据库上下文实例。因此,当工作单元完成的时候,你可以通过在这个数据库上下文实例上调用 SaveChanges 方法来保证相关的所有操作被协调处理。所有这个类需要的就是一个 Save 方法和每个仓储一个的属性。每个仓储属性返回使用相同的数据库上下文对象创建的仓储对象实例。
10-1 执行原始的 SQL 查询
在 EF 中包含有允许你直接将 SQL 命令发送到数据库的 API 方法,你有以下选择: (原文地址:http://www.cnblogs.com/haogj/archive/2012/05/05/2484663.html 或者 http://www.cnblogs.com/haogj/archive/2011/05/09/2040196.html)
- 使用 DbSet.SqlQuery 方法进行查询以返回实体类型。返回的对象类型必须是 DbSet 对象期望的类型。除非你关掉追踪,数据库上下文将自动追踪。( 查看随后关于 AsNoTracking 方法 )
- 使用 DbDatabase.SqlQuery 方法进行查询,返回类型不是实体。返回的数据不会被数据库上下文追踪,即使使用这个方法获取实体类型。
- 使用 DbDatabase.SqlCommand 用于非查询命令。
10-2 非追踪的查询
当数据库上下文从数据库获取数据行然后表示为实体的时候,默认情况下,会保持对内存中实体对象是否与数据库中数据同步的追踪。内存中的数据作为缓存,当你更新实体的时候被用来更新。这个缓存在 Web 应用程序中通常没有必要,因为上下文对象的实例生命期很短 ( 对于每一次请求创建一个新的,然后释放 ) ,在实体被再次使用之前数据库上下文读取的实体对象已经被释放了。
你可以通过 AsNoTracking 方法来指定上下文对象是否追踪实体对象。使用这个方法常见的场景如下:
- 对于查询大量数据的查询来说,关闭追踪可以提高性能。
- 你更希望重新连接一个对象用来更新,尽管基于不同的目的以前获取过同样的对象。由于数据库上下文已经追踪了这个实体,你就不能连接你希望修改的实体。防止出现这种情况的一种方式就是在原来的查询中使用 AsNoTracking 选项。
10.4 使用代理类
当 EF 创建实体对象的时候 ( 例如,在执行查询的时候 ),EF 经常会创建动态生成的派生自实体的代理对象。代理重写了实体的虚拟属性来插入在访问属性的时候自动执行的钩子。例如,这种机制用来支持延迟加载或者关联。
多数时候你并不能察觉使用了代理,除了下面的情况之外:
- 有一些场景,你需要阻止 EF 创建代理实例。例如,序列化非代理的对象实例可能比序列化代理对象实例更加有效。
- 当使用 new 操作符实例化实体的时候,你并没有得到代理实例。这意味着你不能获得诸如延迟加载以及自动追踪的能力。一般没有问题,通常并不需要延迟加载,因为你在创建数据库中并不存在的实体。在将实体标记为 Added 状态的时候,也不需要追踪。然而,如果你需要延迟加载,需要改变追踪,就可以通过 DbSet 类的 Create 方法来创建实体代理对象。
- 可能需要通过代理类型对象实例获取真实实体类型。可以使用 ObjectContext 类的 GetObjectType 方法来通过代理对象获取实际对象。
10.5 禁用变化自动跟踪
EF 通过比较实体的当前值与原始值来决定实体如何变化 ( 依此来决定发送到数据库的更新 )。在查询或者连接的时候原始值被保存起来。导致自动变化跟踪监测的一些方法如下:
- DbSet.Find
- DbSet.Local
- DbSet.Remove
- DbSet.Add
- DbSet.Attach
- DbContext.SaveChanges
- DbContext.GetValidationErrors
- DbContext.Entry
- DbChangeTracker.Entries
如果你跟踪大量的实体,而且在循环中多次调用上面提到的方法,通过使用 AutoDetectChangesEnabled 属性暂时关闭变化检测,可以得到显著的性能提升,更多信息,参见 EF 团队博客的 Automatically Detecting Changes。
10-6 在保存的时候禁用验证
在调用 SaveChanges 方法的时候,在更新到数据库之前,EF 验证所有被修改实体的数据的所有属性。如果你更新了大量的实体,而且你已经验证了数据,这些工作就是不必要的,通过 ValidateOnSaveEntity 属性临时关闭验证可以花费更要的时间保存修改。更多信息,参见 EF 团队博客的 Validation。
10-7 EF 资源链接
更多地 EF 资源,参见如下资源:
- Introduction to the Entity Framework 4.1 (Code First)
- The Entity Framework Code First Class Library API Reference
- Entity Framework FAQ
- The Entity Framework Team Blog
- Entity Framework in the MSDN Library
- Entity Framework in the MSDN Data Developer Center
- Entity Framework Forums on MSDN
- Julie Lerman's blog
- Code First DataAnnotations Attributes
- Maximizing Performance with the Entity Framework in an ASP.NET Web Application
- Profiling Database Activity in the Entity Framework
- Entity Framework Power Tools
下面的 EF 团队的博客提供了教程涉及的更多资源。
- Fluent API Samples. How to customize mapping using fluent API method calls.
- Connections and Models. How to connect to different types of databases.
- Pluggable Conventions. How to change conventions.
- Finding Entities. How to use the
Find
method with composite keys. - Loading Related Entities. Additional options for eager, lazy, and explicit loading.
- Load and AsNoTracking. More on explicit loading.
一些这里提到的博客是关于 CTP 版本的 EF Code First,多数资料是准确的,但是在正式发布版本中会有一些变化。
在 EF 中使用 LINQ 的信息,参见 MSDN 的 LINQ to Entities
使用 MVC 和 EF 的更多信息,参见 MVC Music Store.
在项目创建之后,如何发布的问题,参见 MSDN 的 ASP.NET Deployment Content
目前,EF对存储过程的支持并不完善。存在以下问题:
EF不支持存储过程返回多表联合查询的结果集。
EF仅支持返回返回某个表的全部字段,以便转换成对应的实体。无法支持返回部分字段的情况。
虽然可以正常导入返回标量值的存储过程,但是却没有为我们自动生成相应的实体.cs代码,我们还是无法在代码中直接调用或使用标量存储过程
EF不能直接支持存储过程中Output类型的参数。