[翻译][MVC 5 + EF 6] 12[完结]:高级场景

时间:2022-09-04 22:58:44

原文:Advanced Entity Framework 6 Scenarios for an MVC 5 Web Application

1.执行原生SQL查询

  EF Code First API包含直接传递SQL命令到数据库的方法:

  • 使用DbSet.SqlQuery方法查询返回实体类型。返回的对象必须是DbSet对象所期望的类型,并且数据库上下文会自动追踪它们除非我们关闭了追踪。
  • 使用Database.SqlQuery方法查询返回非实体类型。返回的数据不会被数据库上下文追踪,即使我们为该方法指定实体类型。
  • 使用Database.ExecuteSqlCommand执行非查询操作。

  使用EF的其中一个优点是避免了将我们的代码与特定的存储数据的方法联系的过于紧密。该优点通过为我们产生SQL查询和命令实现,这同时可以让我们不必自己编写这些语句。但在特殊情况下,我们需要执行自己编写的SQL语句,并且这些方法是我们能够处理这些异常。

  长久以来当我们在web应用程序中执行SQL命令时,我们必须采取措施来防止SQL注入攻击 。一种方法是使用参数化的查询来取保web网页提交的字符串不会被解析为SQL命令。在本教程我们将整合用户输入为参数化的查询。

1.1.调用查询返回实体:

  DbSet<TEntity>类提供了一个执行查询返回一个TEntity实体类型的方法。

  修改DepartmentController.cs的Details方法:

public async Task<ActionResult> Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
} // Commenting out original code to show how to use a raw SQL query.
//Department department = await db.Departments.FindAsync(id); // Create and execute raw SQL query.
string query = "SELECT * FROM Department WHERE DepartmentID = @p0";
Department department = await db.Departments.SqlQuery(query, id).SingleOrDefaultAsync(); if (department == null)
{
return HttpNotFound();
}
return View(department);
}

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

1.2.调用查询返回其他类型的对象:

  之前我们在About页面创建了一个student统计表格,显示每个enrollment date的student数量。LINQ代码如下:

var data = from student in db.Students
group student by student.EnrollmentDate into dateGroup
select new EnrollmentDateGroup()
{
EnrollmentDate = dateGroup.Key,
StudentCount = dateGroup.Count()
};

  假设我们想使用SQL代替LINQ。并且需要返回非实体对象,我们就需要使用Database.SqlQuery方法。

  修改HomeController.csAbout方法:

public ActionResult About()
{
// Commenting out LINQ to show how to do the same thing in SQL.
//IQueryable<EnrollmentDateGroup> = from student in db.Students
// group student by student.EnrollmentDate into dateGroup
// select new EnrollmentDateGroup()
// {
// EnrollmentDate = dateGroup.Key,
// StudentCount = dateGroup.Count()
// }; // SQL version of the above LINQ code.
string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
+ "FROM Person "
+ "WHERE Discriminator = 'Student' "
+ "GROUP BY EnrollmentDate";
IEnumerable<EnrollmentDateGroup> data = db.Database.SqlQuery<EnrollmentDateGroup>(query); return View(data.ToList());
}

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

1.3.调用Update查询:

  在CourseContoller.cs添加UpdateCourseCredits方法:

public ActionResult UpdateCourseCredits()
{
return View();
} [HttpPost]
public ActionResult UpdateCourseCredits(int? multiplier)
{
if (multiplier != null)
{
ViewBag.RowsAffected = db.Database.ExecuteSqlCommand("UPDATE Course SET Credits = Credits * {0}", multiplier);
}
return View();
}

  添加视图:

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

@model ContosoUniversity.Models.Course

@{
ViewBag.Title = "UpdateCourseCredits";
} <h2>Update Course Credits</h2> @if (ViewBag.RowsAffected == null)
{
using (Html.BeginForm())
{
<p>
Enter a number to multiply every course's credits by: @Html.TextBox("multiplier")
</p>
<p>
<input type="submit" value="Update" />
</p>
}
}
@if (ViewBag.RowsAffected != null)
{
<p>
Number of rows updated: @ViewBag.RowsAffected
</p>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>

  运行:

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

  更多关于原生SQL查询信息请查看:Raw SQL Queries

2.无追踪(No-Tracking)查询

  当一个数据库上下文搜索到表中的行数据会为他们创建实体对象,默认情况下它会一直追踪内存中的实体是否和数据库中的数据保持一致。内存中的数据作为缓存和更新实体时使用。这种缓存在web应用程序中通常是没有必要的,因为上下文实例通常是短暂的(每次请求都会新建和销毁一个上下文实例),并且通常上下文读取的实例在该实例再次被使用前就已经销毁 。

  我们可以使用AsNoTracking方法禁用内存对象中实体对象的追踪。在下面几个典型的场景中我们可能想要这么做:

  • 一个查询检索大量的数据,关闭追踪会显著的提高性能。
  • 当我们为了更新而附加一个实体时,但是我们之前为了其他的目的已经获取了该实体。因为该实体已经能够被数据库上下文追踪,我们不能附加这个想要修改的实体。这种情况下的一种处理方式是对之前的查询使用AsNoTracking选项。

  AsNoTracking方法使用实例请参考:the earlier version of this tutorial。本教程在Edit方法的实体模型-绑定-创建时没有设置修改标志,所以没有必要使用AsNoTracking

3.检查发送到数据库的SQL

  修改Controllers/CourseController的Index方法,暂时禁用显式加载:

public ActionResult Index()
{
var courses = db.Courses;
var sql = courses.ToString();
return View(courses.ToList());
}

  在return语句设置断点,运行程序,查看sql变量的值:

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

  在Course的Index页面添加下拉列表,用于删选department数据。修改CourseController.csIndex方法:

public ActionResult Index(int? SelectedDepartment)
{
var departments = db.Departments.OrderBy(q => q.Name).ToList();
ViewBag.SelectedDepartment = new SelectList(departments, "DepartmentID", "Name", SelectedDepartment);
int departmentID = SelectedDepartment.GetValueOrDefault(); IQueryable<Course> courses = db.Courses
.Where(c => !SelectedDepartment.HasValue || c.DepartmentID == departmentID)
.OrderBy(d => d.CourseID)
.Include(d => d.Department);
var sql = courses.ToString();
return View(courses.ToList());
}

  重新在return语句设置断点。

  修改Views\Course\Index.cshtml,在table标签之前:

@using (Html.BeginForm())
{
<p>Select Department: @Html.DropDownList("SelectedDepartment","All")
<input type="submit" value="Filter" /></p>
}

  运行:

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

  查看sql变量的值:

SELECT
[Project1].[CourseID] AS [CourseID],
[Project1].[Title] AS [Title],
[Project1].[Credits] AS [Credits],
[Project1].[DepartmentID] AS [DepartmentID],
[Project1].[DepartmentID1] AS [DepartmentID1],
[Project1].[Name] AS [Name],
[Project1].[Budget] AS [Budget],
[Project1].[StartDate] AS [StartDate],
[Project1].[InstructorID] AS [InstructorID],
[Project1].[RowVersion] AS [RowVersion]
FROM ( SELECT
[Extent1].[CourseID] AS [CourseID],
[Extent1].[Title] AS [Title],
[Extent1].[Credits] AS [Credits],
[Extent1].[DepartmentID] AS [DepartmentID],
[Extent2].[DepartmentID] AS [DepartmentID1],
[Extent2].[Name] AS [Name],
[Extent2].[Budget] AS [Budget],
[Extent2].[StartDate] AS [StartDate],
[Extent2].[InstructorID] AS [InstructorID],
[Extent2].[RowVersion] AS [RowVersion]
FROM [dbo].[Course] AS [Extent1]
INNER JOIN [dbo].[Department] AS [Extent2] ON [Extent1].[DepartmentID] = [Extent2].[DepartmentID]
WHERE @p__linq__0 IS NULL OR [Extent1].[DepartmentID] = @p__linq__1
) AS [Project1]
ORDER BY [Project1].[CourseID] ASC

4.仓储和工作单元模式

  许多开发者编写代码实现仓储和工作单元模式作为包装器代码配合EF使用。这些模式的目的是在数据访问层和业务逻辑层之间创建一个抽象层。实现这种模式可以将程序与数据存储变化隔离,并且有利于单元测试和测试驱动开发(TDD)。但是编写额外的代码实现这些模式,对使用EF的程序并不总是最好的选择,原因如下:

  • EF上下文类本身就实现了代码和数据存储的隔离。
  • 当使用EF更新数据库时,EF上下文类可以当作工作单元类。
  • EF6的特性使不编写仓储代码实现TDD变得更加容易。

  更多关于实现仓储和工作单元模式信息请参考:the Entity Framework 5 version of this tutorial series。更多关于在EF6中实现TDD信息请参考:

5.代理类

  当EF创建实体实例时(例如,执行查询时),通常把它们创建可以作为实体代理的动态的派生类型。例如,下面两张调试图片。从第一张可以看到student变量在实例化实体后直接是所期望的Student类型。在第二张图片中,当EF被用来从数据库读取一个student实体时使用的是代理类:

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

  代理类覆盖了实体的一些virtual属性,用来插入钩子在访问属性时自动执行操作。这种机制的一个功能是用于延迟加载。

  大部分情况下,我们不用关心这些代理的使用,除了一些几种情况:

  • 在一些场景中,我们或许想要阻止EF创建代理实例。例如,当我们序列化产生的实体,需要POCO类时,不要代理类。一种解决序列化问题的做法是序列化数据传输对象(DTO)而不是实体对象,请参考:Using Web API with Entity Framework。另一种方法是:disable proxy creation
  • 当我们使用new实例化一个实体类时,不会产生代理实例。这意味着我们不使用 延迟加载和自动修改追踪功能。这通常是没有问题的,我们通常不需要延迟加载,因为我们不是在数据库中创建一个实体,并且我们通常也不需要修改追踪,如果我们特别标记实体为Added。然而,我们需要延迟加载和修改追踪,我们可以使用DbSet的Create方法创建使用代理的实体实例。
  • 我们想要从代理类型获取实际的实体类型。我们可以使用ObjectContext类的GetObjectType方法从代理类型实例获取实际的实体类型。

  更多信息请参考:Working with Proxies

6.自动检测改变

  EF根据比较实体原始值和当前值实体来确定实体是否被改变(哪个update需要被发送的数据库)。原始值在实体被查询或附加时存储。下面的一些方法会自动检测改变:

  • DbSet.Find
  • DbSet.Local
  • DbSet.Remove
  • DbSet.Add
  • DbSet.Attach
  • DbContext.SaveChanges
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries

  当我们追踪大量的实体,并且在循环中多次调用上面的方法时,使用AutoDetectChangesEnabled属性暂时关闭自动检测改变功能,来获取显著的性能提升。更多信息请查看:Automatically Detecting Changes

7.EF Power Tools

  Entity Framework Power Tools是Visual Studio内置的用来创建本教程显示的数据模型图。该工具同样具有其他功能,例如根据已经存在的数据库的表产生实体类,这样就可以使用Code First。安装此工具后,上下文菜单会出现一些其他的选项。例如,当我们在Solution Explorer的上下文类中右键,会出现产生图表选项。当我们使用Code First时,我们不能改变图表中的数据模型,但是我们可以移动它们的位置,让它们变得容易理解。

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

[翻译][MVC 5 + EF 6] 12[完结]:高级场景

8.EF源码

  EF6的源码在http://entityframework.codeplex.com/。除了源码,我们也可以获取nightly builds, issue tracking, feature specs, design meeting notes等。我们可以修复bug,并且贡献我们自己的EF6增强代码。

  尽管EF是开源的,但是它是微软完全支持的产品。微软的EF团队控制接受哪些贡献的代码并对所有的更改进行测试以确保每个发行版本的质量。

9.总结

  更多关于如何使用EF处理数据的信息,请查看:EF documentation page on MSDNASP.NET Data Access - Recommended Resources

  更多关于如何部署编译后的web程序,请查看:ASP.NET Web Deployment - Recommended Resources

  更多关于MVC的其他话题,请查看:ASP.NET MVC - Recommended Resources

10.常见错误以及它们的解决方案或者替代方案

Cannot create/shadow copy

Error Message:

Cannot create/shadow copy '<filename>' when that file already exists.

Solution

等待几秒并刷新页面。

Update-Database not recognized

Error Message (from the Update-Database command in the PMC):

The term 'Update-Database' is not recognized as the
name of a cmdlet, function, script file, or operable program. Check the spelling
of the name, or if a path was included, verify that the path is correct and try
again.

Solution

关闭Visual Studio,重启项目并重试。

Validation failed

Error Message (from the Update-Database command in the PMC):

Validation failed for one or more entities. See
'EntityValidationErrors' property for more details.

Solution

这个问题的的其中一个原因是种子方法运行时验证错误.  查看Seeding and Debugging Entity Framework (EF) DBs关于调试Seed方法的建议。

HTTP 500.19 error

Error Message:

HTTP Error 500.19 - Internal Server Error
The requested page cannot be accessed because the related configuration data for
the page is invalid.

Solution

这个错误的一个原因是解决方案有多个副本,并且这些副本使用相同的端口号。通常我们可以通过关闭所有的Visual Studio,然后重启项目来解决此问题。如果上述方法不奏效,就改变端口号。右键项目,点击properties,选择Web标签在Project Url修改端口号。

Error locating SQL Server instance

Error Message:

A network-related or instance-specific error occurred
while establishing a connection to SQL Server. The server was not found or was
not accessible. Verify that the instance name is correct and that SQL Server is
configured to allow remote connections. (provider: SQL Network Interfaces,
error: 26 - Error Locating Server/Instance Specified)

Solution

查看连接字符串。如果手动删除了数据库,则修改连接字符串的数据库名。

[翻译][MVC 5 + EF 6] 12[完结]:高级场景的更多相关文章

  1. &lbrack;翻译&rsqb;&lbrack;MVC 5 &plus; EF 6&rsqb; 5:Code First数据库迁移与程序部署

    原文:Code First Migrations and Deployment with the Entity Framework in an ASP.NET MVC Application 1.启用 ...

  2. &lbrack;翻译&rsqb;&lbrack;MVC 5 &plus; EF 6&rsqb; 11:实现继承

    原文:Implementing Inheritance with the Entity Framework 6 in an ASP.NET MVC 5 Application 1.选择继承映射到数据库 ...

  3. &lbrack;翻译&rsqb;&lbrack;MVC 5 &plus; EF 6&rsqb; 10:处理并发

    原文:Handling Concurrency with the Entity Framework 6 in an ASP.NET MVC 5 Application 1.并发冲突: 当一个用户编辑一 ...

  4. &lbrack;翻译&rsqb;&lbrack;MVC 5 &plus; EF 6&rsqb; 9:异步和存储过程

    原文:Async and Stored Procedures with the Entity Framework in an ASP.NET MVC Application 1.为什么使用异步代码: ...

  5. &lbrack;翻译&rsqb;&lbrack;MVC 5 &plus; EF 6&rsqb; 8:更新相关数据

    原文:Updating Related Data with the Entity Framework in an ASP.NET MVC Application 1.定制Course的Create和E ...

  6. &lbrack;翻译&rsqb;&lbrack;MVC 5 &plus; EF 6&rsqb; 7:加载相关数据

    原文:Reading Related Data with the Entity Framework in an ASP.NET MVC Application 1.延迟(Lazy)加载.预先(Eage ...

  7. &lbrack;翻译&rsqb;&lbrack;MVC 5 &plus; EF 6&rsqb; 6:创建更复杂的数据模型

    原文:Creating a More Complex Data Model for an ASP.NET MVC Application 前面的教程中,我们使用的是由三个实体组成的简单的数据模型.在本 ...

  8. &lbrack;翻译&rsqb;&lbrack;MVC 5 &plus; EF 6&rsqb; 4:弹性连接和命令拦截

    原文:Connection Resiliency and Command Interception with the Entity Framework in an ASP.NET MVC Applic ...

  9. &lbrack;翻译&rsqb;&lbrack;MVC 5 &plus; EF 6&rsqb; 3:排序、过滤、分页

    原文:Sorting, Filtering, and Paging with the Entity Framework in an ASP.NET MVC Application 1.添加排序: 1. ...

随机推荐

  1. 转行进入IT前端,目标全栈

    选择进入IT这个行业,未来充满着挑战,未来充满着机遇,互联网已经走入高速稳定的发展正轨了,前端大时代也已经到来了.前端之路怎么走,前端需要不停地学习新知识,跟紧时代的步伐,成为全栈工程师是前端的目标. ...

  2. ArcGIS Server开发教程系列(7)使用ArcGIS API for Javascript-Hello World

    ArcGIS API for Javascript  API下载地址:http://support.esrichina-bj.cn/2011/0223/960.html 选择最新的下载就好了,目前是3 ...

  3. Eclipse配置PyDev插件来实现python开发环境

    1.安装python解释器(完成的略过) 2.安装PyDev: 首先需要去Eclipse官网下载:http://www.eclipse.org/,Eclipse需要JDK支持,如果Eclipse无法正 ...

  4. Git 忽略文件

    在Git中如果想忽略掉某个文件,不让这个文件提交到版本库中,可以使用修改 .gitignore 文件的方法.这个文件每一行保存了一个匹配的规则例如: # 此为注释 – 将被 Git 忽略 *.a    ...

  5. jquery 显示&OpenCurlyDoubleQuote;加载状态 结束”

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. &ast;&lbrack;hackerrank&rsqb;Girlfriend &amp&semi; Necklace

    https://www.hackerrank.com/contests/w8/challenges/gneck 有点意思.是DP,最优解包含最优子问题.F(X)=F(X-1)+F(X-3).因为F(X ...

  7. Can’t create handler inside thread that has not called Looper&period;prepare&lpar;&rpar;

    1)在Android 2.3以前,为防止ANR(Application Not Responding),Google是不赞成将网络连接等一系列耗时操作直接放到应用主线程进行的,推荐将这类操作放在子线程 ...

  8. 支付平台程序,支付程序,网络pos程序,api接口程序,锋锐支付平台程序开发领导者!

    支付平台程序,支付程序,网络pos程序,api接口程序,锋锐支付平台程序开发领导者! 锋锐支付平台程序(www.100freenet.com)隶属于盐城市沐良商贸有限公司(沈阳杰速网络科技有限公司旗下 ...

  9. 初识java这个小姑娘(二)

    妙解垃圾回收机制 周一,早高峰. 一段考验一个人耐力.智力.开车技术以及脾气的路. 我把车开进了一个没有红绿灯的丁字路口,然后就没有然后了. 来自三个方向的大车小车开始在不大的一块空间里开始互相斗智斗 ...

  10. java多线程 生产者和消费者 lock

    package com.atguigu.thread.lock; import java.util.concurrent.locks.Condition; import java.util.concu ...