记一次EFCore类型转换错误及解决方案
一 背景
今天在使用EntityFrameworkCore 查询的时候在调试的时候总是提示如下错误:Unable to cast object of type 'System.Data.SqlTypes.SqlString' to type 'System.Data.SqlTypes.SqlGuid' 第一次看这个报错肯定是数据库实体和EFCore中定义的某种类型不匹配从而导致类型转换错误,但是业务涉及到这么多的实体Entity,那么到底是哪里类型无法匹配呢?所以第一步肯定是调试代码,然后看报错信息,这里我们首先贴出完整的报错信息,从而方便自己分析具体问题。
System.InvalidCastException: Unable to cast object of type 'System.Data.SqlTypes.SqlString' to type 'System.Data.SqlTypes.SqlGuid'.
at System.Data.SqlClient.SqlBuffer.get_SqlGuid()
at System.Data.SqlClient.SqlDataReader.GetGuid(Int32 i)
at lambda_method(Closure , DbDataReader )
at Microsoft.EntityFrameworkCore.Storage.Internal.TypedRelationalValueBufferFactory.Create(DbDataReader dataReader)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func` operation, Func` verifySucceeded)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`.Enumerator.MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider._TrackEntities[TOut,TIn](IEnumerable` results, QueryContext queryContext, IList` entityTrackingInfos, IList` entityAccessors)+MoveNext()
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`.EnumeratorExceptionInterceptor.MoveNext()
at System.Linq.Lookup`.CreateForJoin(IEnumerable` source, Func` keySelector, IEqualityComparer` comparer)
at System.Linq.Enumerable.JoinIterator[TOuter,TInner,TKey,TResult](IEnumerable` outer, IEnumerable` inner, Func` outerKeySelector, Func` innerKeySelector, Func` resultSelector, IEqualityComparer` comparer)+MoveNext()
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable` source, Func` keySelector, Func` elementSelector, IEqualityComparer` comparer)
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable` source, Func` keySelector, Func` elementSelector)
at Sunlight.Dcs.Application.Sales.SalesOrder.DegradedVehicleContracts.DegradedVehicleContractService.QueryByIdAsync(Int32 id) in E:\\sales-service\src\sales.orders\Application.Sales.Orders\SalesOrder\DegradedVehicleContracts\DegradedVehicleContractService.cs:line
at Abp.Threading.InternalAsyncHelper.AwaitTaskWithPostActionAndFinallyAndGetResult[T](Task` actualReturnValue, Func` postAction, Action` finalAction)
at Sunlight.Dcs.WebApi.Sales.Controllers.Orders.DegradedVehicleContractController.QueryById(Int32 id) in E:\\sales-service\src\WebApi.Sales\Controllers\Orders\DegradedVehicleContractController.cs:line
at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at System.Threading.Tasks.ValueTask`.get_Result()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync()
二 解决方案
有了上面的报错信息我们就能够知道大致方向,接下来我们首先来看看报错信息的这段代码。
public async Task<SimpleDegradedVehicleContractOutput> QueryByIdAsync(int id) {
var queryResult = await _degradedVehicleContractRepository.GetAll()
.Include(d => d.DegradedVehicleContractDetails)
.SingleOrDefaultAsync(mp => mp.Id == id);
if (queryResult == null)
throw new ValidationException($"当前Id为:{id}的降级车合同没有找到");
var result = _objectMapper.Map<SimpleDegradedVehicleContractOutput>(queryResult);
result.Details = _objectMapper.Map<List<DegradedVehicleContractDetailDto>>(queryResult.DegradedVehicleContractDetails);
//获取ProductId
var productIds = queryResult.DegradedVehicleContractDetails.Select(d => d.ProductId).Distinct().ToArray();
//车辆Id
var vinLists = queryResult.DegradedVehicleContractDetails.Select(r => r.Vin).Distinct().ToArray();
var detailResult = (from detail in queryResult.DegradedVehicleContractDetails
join product in _productRepository.GetAll().Where(p => productIds.Contains(p.Id))
on detail.ProductId equals product.Id
join vehicleDict in _vehicleInformationRepository.GetAll().Where(v => vinLists.Contains(v.Vin))
on detail.Vin equals vehicleDict.Vin
select new {
ProductId = product.Id,
product.ProductType,
VehicleId = vehicleDict.Id,
detail.Vin
}).ToDictionary(r => Tuple.Create(r.ProductId, r.Vin), r => new { r.ProductType, r.VehicleId });
result.Details.ForEach(r => {
r.ProductType = detailResult[Tuple.Create(r.ProductId, r.Vin)]?.ProductType;
r.VehicleId = detailResult[Tuple.Create(r.ProductId, r.Vin)].VehicleId;
});
return result;
}
2.1 定位报错位置
通过直接对代码进行调试,我们发现只要代码执行获取detailResult这一步的时候就会出现上面的错误,那么到这里我们就可以推断错误的地方就在这里了,所以后面我们的重点就是分析这段代码。
2.2 定位产生错误的表名称
这里就是利用前面的Include方法来查询到queryResult结果,然后利用queryResult.DegradedVehicleContractDetails来和Product以及VehicleInformation表来做inner join,这里你可能对_productRepository以及_vehicleInformationRepository这个局部变量不是十分熟悉,那么我们先来看看这个局部变量的定义以及初始化。
private readonly IRepository<Product> _productRepository;
private readonly IRepository<VehicleInformation> _vehicleInformationRepository;
上面是局部变量的定义,在我们的示例代码中我们使用ABP框架来作为整个项目代码的基础框架,这里的IRepository接口来自于ABP框架中定义的接口类型用于直接操作数据库表,这里具体的实现就是通过构造函数来进行注入的,具体请参考下面的实例。
public DegradedVehicleContractService(IObjectMapper objectMapper,
IRepository<DegradedVehicleContract> degradedVehicleContractRepository,
IRepository<Product> productRepository,
IRepository<VehicleInformation> vehicleInformationRepository,
IRepository<Company> companyRepository,
IDegradedVehicleContractManager degradedVehicleContractManager,
IRepository<ProductAffiProductCategory> productAffiProductCategoryRepository,
IRepository<ProductCategoryBusinessDomain> productCategoryBusinessDomainRepository,
IRepository<TiledProductCategory> tiledProductCategoryRepository,
IRepository<BusinessDomain> businessDomainRepository,
IMapper autoMapper) {
_objectMapper = objectMapper;
_degradedVehicleContractRepository = degradedVehicleContractRepository;
_productRepository = productRepository;
_vehicleInformationRepository = vehicleInformationRepository;
_companyRepository = companyRepository;
_degradedVehicleContractManager = degradedVehicleContractManager;
_productAffiProductCategoryRepository = productAffiProductCategoryRepository;
_productCategoryBusinessDomainRepository = productCategoryBusinessDomainRepository;
_tiledProductCategoryRepository = tiledProductCategoryRepository;
_businessDomainRepository = businessDomainRepository;
_autoMapper = autoMapper;
}
有了上面的注释,相信你对上面那部分代码可以有更加深入的理解,回到正题,这里到底是Product实体中的问题还是VehicleInformation实体中存在问题呢?我们首先将
join vehicleDict in _vehicleInformationRepository.GetAll().Where(v => vinLists.Contains(v.Vin)) on detail.Vin equals vehicleDict.Vin
这个部分注释掉,然后再调试代码,我们发现代码竟然不报错了,然后初步判断VehicleInformation这张表里面有问题,然后我们接着注释掉第二部分而保留第三部分,其中注释的部分代码为:
join product in _productRepository.GetAll().Where(p => productIds.Contains(p.Id)) on detail.ProductId equals product.Id
经过这部分的操作以后,我们发现执行报错,有了这两步的验证之后我们更加确认是VehicleInformation表中存在类型不匹配的问题,然后我们接着往下进行分析。
2.3 定位报错字段
既然报错信息中是SqlTypes.SqlString'和SqlTypes.SqlGuid之间的转换有问题那么我们断定有一个字段数据库中定义的类型和实体中定义的类型不匹配,而且其中有一种是guid类型,由于我们的实体中guid类型的字段要少于string类型的字段,所以我们首先从guid类型下手,我们看看VehicleInformation中是否有哪种guid类型和数据库不匹配。然后还真的发现了这个类型 public Guid UnionId { get; set; },在我们的实体中定义了一个guid类型的字段UnionId这个是在迁移过程迁移到数据库的,然后我们来查看数据库中的类型。

图一 数据库中UnionId字段类型
通过查询数据库我们发现数据库中字段UnionId被定义成了varchar(50)类型,明显在和代码中guid类型进行匹配的时候会发生错误,后来我很疑惑我们的开发模式是采用Code First来进行开发的,现有实体然后再通过Migration进行数据库迁移的,应该不会出现这样的错误,事后得知是另外一位同事在开发的过程中手动去更改了这个实体的类型从而导致了这个问题,最后更改数据库UnionId字段类型,然后发现错误消失,至此问题解决。
总结
这篇文章写作的主要目的是如果从一个大致方向来一步步去缩小错误范围,最终来一步步找出错误的根源,最终来解决问题,在这个过程中通过注释掉部分代码来缩小判断范围确实非常有用,另外用到的一个重要的知道思想就是“大胆假设小心求证”的思想来一步步分析问题,然后找到问题的根源,最终解决问题,所以有了上述分析问题解决问题的方法,我们就能够以后解决这一类型的问题,真正做到掌握这一类型问题的解决方法。
记一次EFCore类型转换错误及解决方案的更多相关文章
- spring参数类型异常输出(二), SpringMvc参数类型转换错误输出(二)
spring参数类型异常输出(二), SpringMvc参数类型转换错误输出(二) >>>>>>>>>>>>>>&g ...
- 大数据技术之_08_Hive学习_05_Hive实战之谷粒影音(ETL+TopN)+常见错误及解决方案
第10章 Hive实战之谷粒影音10.1 需求描述10.2 项目10.2.1 数据结构10.2.2 ETL原始数据10.3 准备工作10.3.1 创建表10.3.2 导入ETL后的数据到原始表10.3 ...
- 关于Redis-存Long取Integer类型转换错误的问题;String对象被转义的问题
背景 最近遇到了两个Redis相关的问题,趁着清明假期,梳理整理. 1.存入Long类型对象,在代码中使用Long类型接收,结果报类型转换错误. 2.String对象的反序列化问题,直接在Redis服 ...
- 创建或打开解决方案时提示"DotNetCore.1.0.1-SDK.1.0.0.Preview2-003131-x86"错误的解决方案
提示"DotNetCore.1.0.1-SDK.1.0.0.Preview2-003131-x86"错误的解决方案: 1.检查是否有C:\Program Files (x86)\d ...
- 针对每种Windows Server 操作Excel、Word等Office组件遇到“ComException"、”80070005“等COM错误的解决方案大汇总
以下所有Excel错误的解决方案,同样适用于Word.PowerPoint等Office产品. 以下解决方案中,如果出现"安装Excel组件",是适用于遇到Excel错误的.如果是 ...
- 《javascript》高级程序设计——类型转换错误
容易发生类型转换错误的另一个地方,就是流控制语句.像if之类的语句在确定下一步操作之前,会自动把任何值转换成布尔值.尤其是if语句,如果使用不当,最容易出错.来看下面的例子. function con ...
- SQL Server附加数据库时报1813错误的解决方案
SQL Server附加数据库时报1813错误的解决方案 无法打开新数据库 'ASR'.CREATE DATABASE 中止. 文件激活失败.物理文件名称'E:\SqlServer\MSSQL\D ...
- 关于Oracle出现listener refused the connection with the ORA-12505错误,解决方案
出现listener refused the connection with the ORA-12505错误,解决方案: 1.首先重启一下电脑,释放被占用的1521端口 2.重启后打开Oracle D ...
- [原创]java WEB学习笔记67:Struts2 学习之路-- 类型转换概述, 类型转换错误修改,如何自定义类型转换器
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
随机推荐
- 请解释或描述一下Django的架构
对于Django框架遵循MVC设计,并且有一个专有名词:MVT M全拼为Model,与MVC中的M功能相同,负责数据处理,内嵌了ORM框架 V全拼为View,与MVC中的C功能相同,接收HttpReq ...
- [SDOI2009][BZOJ 1876]SuperGCD
Description Sheng bill有着惊人的心算能力,甚至能用大脑计算出两个巨大的数的GCD(最大公约 数)!因此他经常和别人比 赛计算GCD.有一天Sheng bill很嚣张地找到了你,并 ...
- 【知识点】同样是消息队列,Kafka凭什么速度那么快?
同样是消息队列,Kafka凭什么速度那么快? 作者 | MrZhangxd Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafk ...
- leaflet常用插件库
1.常用地图切换加载(osm.google.baidu.gaode.tianditu.etc)https://github.com/htoooth/Leaflet.ChineseTmsProvider ...
- 利用Wireshark抓取并分析OpenFlow协议报文
OpenFlow 交换机与控制器交互步骤 1. 利用Mininet仿真平台构建如下图所示的网络拓扑,配置主机h1和h2的IP地址(h1:10.0.0.1,h2:10.0.0.2),测试两台主机之间的网 ...
- DELPHI控件升级
DELPHI控件升级 1)DELPHI里面卸载旧版控件: 2)WINDOWS里面卸载旧版控件: 3)删除旧版控件所在安装文件夹: 4)删除旧版的DCU,DCP,BPL文件: 5)安装新版控件: 6)程 ...
- 对pdf中的图片进行自动识别
对pdf中的图片进行自动识别 商务合作,科技咨询,版权转让:向日葵,135—4855__4328,xiexiaokui#qq.com 原理:增强扫描 效果:自动识别所有图片中的文字,可以选择.复制,进 ...
- Kali Linux软件更新日报20190623
Kali Linux软件更新日报20190623 (1)payloadsallthethings更新到2.0-0kali4,此次更新增加帮助脚本. (2)tftpd32更新到4.50-0kali2 ...
- ArrayAdapter和ListView
利用ArrayAdapter向ListView中添加数据 <?xml version="1.0" encoding="utf-8"?> <Li ...
- Docs-.NET-C#-指南-语言参考-关键字-值类型:内置数值转换
ylbtech-Docs-.NET-C#-指南-语言参考-关键字-值类型:内置数值转换 1.返回顶部 1. 内置数值转换(C# 参考) 2019/10/22 C# 提供了一组整型和浮点数值类型. 任何 ...