WebApi2 GET 总是说发生了错误
WebApi2 GET always says error has occurred
我的 Web API 2 项目中有一个简单的 GET 方法,它通过 Entity Framework 查询我的 Microsof tSQL 数据库,但总是 returns 出错。如果我在调试器中逐步执行它,则不会命中异常。它实际上看起来很干净地离开了方法。我很困惑。
[Route("ar")]
public IHttpActionResult GetAuditArs(int auditId)
{
using (var context = new LabSOREntities()) {
try {
var ars = from r in context.SMBWA_Audit_AR
where r.SMBWA_Audit_Id == auditId
select r;
var ret = Ok(ars.ToArray());
return ret;
} catch (Exception ex) {
return BadRequest($"Something went wrong: {ex.Message}");
}
}
}
数据库中有一行,我看到我的 ars.ToArray()
说其中只有一个元素。我该如何调试它,因为它在爆炸时离开了我的方法?
如果我只是通过浏览器访问该端点,我会得到:
<Error>
<Message>An error has occurred.</Message>
</Error>
问题在于您正在从 API 调用中 returning 实体。在幕后,WebAPI 必须序列化正在 returned 的数据,因为它这样做会命中任何延迟加载引用属性并尝试加载它们。由于您在 using 块中实例化范围内的数据库上下文,实体将在序列化之前从上下文中孤立出来,因此 EF 将抛出数据库上下文不可用的异常。
验证行为的选项
- 急切加载 "SMBWA_Audit_AR" class 中的所有参考资料。这应该可以消除错误并确认延迟加载序列化问题。
var ars = context.SMBWA_Audit_AR
.Include(x => x.Reference1)
.Include(x => x.Reference2) // etc. where Reference1/2 are related entites to your audit record. If you have a lot of references, that is a lot of includes...
.Where(x => x.SMBWA_Audit_Id = auditId)
.ToArray();
为了避免这样的问题,并且 cost/time 预先加载我推荐的所有内容,我建议使用 POCO DTO/ViewModel 来 return 有关这些审计记录的详细信息。然后你可以 .Select() POCO需要的字段。这避免了延迟加载序列化问题,并且将您从 EF 的查询优化为 return 仅需要的数据,而不是整个对象图。
例如:
如果您需要审计编号、名称和备注字段显示在审计摘要列表中:
public class AuditSummary
{
public int AuditID {get; set;}
public string AuditorName {get; set;}
public string Notes {get; set;}
// You can add whatever fields are needed from the Audit or related entities... Including collections of other DTOs for related entites, or summary details like Counts etc..
}
var ars = context.SMBWA_Audit_AR
.Where(x => x.SMBWA_Audit_Id = auditId)
.Select(x => new AuditSummary
{
AuditId = x.AuditId,
AuditorName = x.AuditedBy.Name, //Example getting reference details..
Notes = x.Notes
}).ToArray();
Return 反映消费者需求的模型。这可以避免 EF 出现问题并确保您的查询高效。
- 使用 IoC 容器(Unity/Autofac 等)将 DbContext 限定在请求范围内。这似乎是一个可行的选择,但不推荐这样做。虽然它会避免错误,但当序列化程序遍历您的实体时,您的 DbContext 将通过 ID 一次查询每个单独的依赖项。当应用程序 运行 检测延迟加载调用时,您可以通过 运行 针对数据库的分析器看到此行为。它最终会起作用,但是会很慢。
作为一般规则,不要 return 来自 Web API 或 MVC 控制器方法的实体,以避免序列化程序出现错误和性能问题。
我的 Web API 2 项目中有一个简单的 GET 方法,它通过 Entity Framework 查询我的 Microsof tSQL 数据库,但总是 returns 出错。如果我在调试器中逐步执行它,则不会命中异常。它实际上看起来很干净地离开了方法。我很困惑。
[Route("ar")]
public IHttpActionResult GetAuditArs(int auditId)
{
using (var context = new LabSOREntities()) {
try {
var ars = from r in context.SMBWA_Audit_AR
where r.SMBWA_Audit_Id == auditId
select r;
var ret = Ok(ars.ToArray());
return ret;
} catch (Exception ex) {
return BadRequest($"Something went wrong: {ex.Message}");
}
}
}
数据库中有一行,我看到我的 ars.ToArray()
说其中只有一个元素。我该如何调试它,因为它在爆炸时离开了我的方法?
如果我只是通过浏览器访问该端点,我会得到:
<Error>
<Message>An error has occurred.</Message>
</Error>
问题在于您正在从 API 调用中 returning 实体。在幕后,WebAPI 必须序列化正在 returned 的数据,因为它这样做会命中任何延迟加载引用属性并尝试加载它们。由于您在 using 块中实例化范围内的数据库上下文,实体将在序列化之前从上下文中孤立出来,因此 EF 将抛出数据库上下文不可用的异常。
验证行为的选项 - 急切加载 "SMBWA_Audit_AR" class 中的所有参考资料。这应该可以消除错误并确认延迟加载序列化问题。
var ars = context.SMBWA_Audit_AR
.Include(x => x.Reference1)
.Include(x => x.Reference2) // etc. where Reference1/2 are related entites to your audit record. If you have a lot of references, that is a lot of includes...
.Where(x => x.SMBWA_Audit_Id = auditId)
.ToArray();
为了避免这样的问题,并且 cost/time 预先加载我推荐的所有内容,我建议使用 POCO DTO/ViewModel 来 return 有关这些审计记录的详细信息。然后你可以 .Select() POCO需要的字段。这避免了延迟加载序列化问题,并且将您从 EF 的查询优化为 return 仅需要的数据,而不是整个对象图。
例如: 如果您需要审计编号、名称和备注字段显示在审计摘要列表中:
public class AuditSummary
{
public int AuditID {get; set;}
public string AuditorName {get; set;}
public string Notes {get; set;}
// You can add whatever fields are needed from the Audit or related entities... Including collections of other DTOs for related entites, or summary details like Counts etc..
}
var ars = context.SMBWA_Audit_AR
.Where(x => x.SMBWA_Audit_Id = auditId)
.Select(x => new AuditSummary
{
AuditId = x.AuditId,
AuditorName = x.AuditedBy.Name, //Example getting reference details..
Notes = x.Notes
}).ToArray();
Return 反映消费者需求的模型。这可以避免 EF 出现问题并确保您的查询高效。
- 使用 IoC 容器(Unity/Autofac 等)将 DbContext 限定在请求范围内。这似乎是一个可行的选择,但不推荐这样做。虽然它会避免错误,但当序列化程序遍历您的实体时,您的 DbContext 将通过 ID 一次查询每个单独的依赖项。当应用程序 运行 检测延迟加载调用时,您可以通过 运行 针对数据库的分析器看到此行为。它最终会起作用,但是会很慢。
作为一般规则,不要 return 来自 Web API 或 MVC 控制器方法的实体,以避免序列化程序出现错误和性能问题。