记一次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`1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider._TrackEntities[TOut,TIn](IEnumerable`1 results, QueryContext queryContext, IList`1 entityTrackingInfos, IList`1 entityAccessors)+MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
   at System.Linq.Lookup`2.CreateForJoin(IEnumerable`1 source, Func`2 keySelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.JoinIterator[TOuter,TInner,TKey,TResult](IEnumerable`1 outer, IEnumerable`1 inner, Func`2 outerKeySelector, Func`2 innerKeySelector, Func`3 resultSelector, IEqualityComparer`1 comparer)+MoveNext()
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
   at Sunlight.Dcs.Application.Sales.SalesOrder.DegradedVehicleContracts.DegradedVehicleContractService.QueryByIdAsync(Int32 id) in E:\63318\sales-service\src\sales.orders\Application.Sales.Orders\SalesOrder\DegradedVehicleContracts\DegradedVehicleContractService.cs:line 99
   at Abp.Threading.InternalAsyncHelper.AwaitTaskWithPostActionAndFinallyAndGetResult[T](Task`1 actualReturnValue, Func`1 postAction, Action`1 finalAction)
   at Sunlight.Dcs.WebApi.Sales.Controllers.Orders.DegradedVehicleContractController.QueryById(Int32 id) in E:\63318\sales-service\src\WebApi.Sales\Controllers\Orders\DegradedVehicleContractController.cs:line 50
   at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at System.Threading.Tasks.ValueTask`1.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字段类型,然后发现错误消失,至此问题解决。

  总结

  这篇文章写作的主要目的是如果从一个大致方向来一步步去缩小错误范围,最终来一步步找出错误的根源,最终来解决问题,在这个过程中通过注释掉部分代码来缩小判断范围确实非常有用,另外用到的一个重要的知道思想就是“大胆假设小心求证”的思想来一步步分析问题,然后找到问题的根源,最终解决问题,所以有了上述分析问题解决问题的方法,我们就能够以后解决这一类型的问题,真正做到掌握这一类型问题的解决方法。

原文地址:https://www.cnblogs.com/seekdream/p/11748975.html

时间: 2024-09-29 21:14:59

记一次EFCore类型转换错误及解决方案的相关文章

记录我在百度地图开发和ArcGIS for Android开发时出现的一些错误及解决方案(后续更新)

[1]The import com.baidu.mapapi.map.Geometry conflicts with a type defined in the same file 解决:百度api包下的Geometry和某个类名相冲突,将类名换成另外的名字,不要和百度相关类里面的类名相同 [2]java.lang.ClassCastException: 解决:类型转换错误.查看Test_Geometry项目的Mainfest.xml清单文件,在<applicaiton>标签里面少了对Myap

大数据技术之_05_Hadoop学习_04_MapReduce_Hadoop企业优化(重中之重)+HDFS小文件优化方法+MapReduce扩展案例+倒排索引案例(多job串联)+TopN案例+找博客共同粉丝案例+常见错误及解决方案

第6章 Hadoop企业优化(重中之重)6.1 MapReduce 跑的慢的原因6.2 MapReduce优化方法6.2.1 数据输入6.2.2 Map阶段6.2.3 Reduce阶段6.2.4 I/O传输6.2.5 数据倾斜问题6.2.6 常用的调优参数6.3 HDFS小文件优化方法6.3.1 HDFS小文件弊端6.3.2 HDFS小文件解决方案第7章 MapReduce扩展案例7.1 倒排索引案例(多job串联)7.2 TopN案例7.3 找博客共同粉丝案例第8章 常见错误及解决方案 第6章

SVN:cleanup failed to process thefollowing paths 错误的解决方案

在使用TortoiseSVN工具执行Cleanup操作时经常出现Cleanup failed to process the following paths的错误,具体如下图: 网上搜索了一下,找到了解决办法: TortoiseSVN客户端1.7版以前的加锁文件时一个lock后缀的文件,直接在报错目录的.svn目录下删除即可. TortoiseSVN客户端1.7版以前(含1.7版本)的加锁信息是一条记录放在报错目录下的.db文件里面,用可以SQLite文件的工具将改文件打开,然后删除那条lock记

oracle11g 数据库导出报“ EXP-00003: 未找到段 (0,0) 的存储定义”错误的解决方案

导出oracle11.2.0.2的服务器的数据时,报"EXP-00003: 未找到段 (0,0) 的存储定义"错误.初步分析是由于数据表是空表导致该问题. Oracle 11G在用EXPORT导出时,空表不能导出 11GR2中有个新特性,当表无数据时,不分配segment,以节省空间 解决方法:一. insert一行,再rollback就产生segment了. 该方法是在在空表中插入数据,再删除,则产生segment.导出时则可导出空表.二. 设置deferred_segment_cr

配置系统未能初始化 错误的解决方案

今天修改了App.config,结果运行的时候出现了 "配置系统未能初始化" 的错误.找了半天才发现是下面的原因造成的: MSDN里写到"如果配置文件中包含 configSections 元素,则 configSections 元素必须是 configuration 元素的第一个子元素.". 配置系统未能初始化 错误的解决方案,布布扣,bubuko.com

记:Android 服务站 问题记录与解决方案

1.dnsmasq域名劫持失败: 现象: 开启wifi热点后确实执行了dnsmasq命令,但是域名劫持失败. 原因: wifi热点开启命令执行后直接执行了dnsmasq命令,当热点开启过程时间很短时域名劫持可以成功:但是启动稍慢即会被系统启动的dnsmasq命令给杀掉并覆盖:所以会导致无效. 解决方案: 开启dnsmasq前定时检查wifi热点是否已经开启成功,成功后再kill掉系统启动的dnsmasq进程,再使用自定义的配置文件执行dnsmasq命令. 2.wifi热点启动频繁失败: 现象:

DICOM:dcm4che开源项目导入Eclipse编译错误问题解决方案

背景: 马上跨年了,记得小时候元旦都不是事儿,怎么长大了长大了感觉越来越热闹了反而.各种饭局.各种party,有点hold不住.还是回来整理一下这一年的笔记比较舒服,比较有收获.看了看这个月竟然发文量还不足四篇,遂将Evernote中的一篇老文贴出来水一下,赶巧解决一下之前网友邮件中遇到的相关问题. dcm4che开源项目: dcm4che3是github上的开源项目,是java实现的DICOM开源库,采用Maven进行构建.整个工程在Windows文件系统下直接mvn install可以顺利成

在Windows8.1中通过IIS发布网站产生HTTP Error 503错误的解决方案

1.解决IIS下网站Bin目录中32位DLL不能使用,如图所示 2.解决通过IIS浏览网站,出现Http503的问题,如图所示 在Windows8.1中通过IIS发布网站产生HTTP Error 503错误的解决方案,布布扣,bubuko.com

安装 SQL SERVER 2008 必须使用 &quot;角色管理工具&quot; 错误 的 解决方案 (转)

刚在服务器(Win2008)上安装SqlServer2008的时候出现了这么一个报错——必须使用“角色管理工具”安装或配置Microsoft .NET Framework 3.5 SP1.一开始以为是没装3.5,后来看了一下服务器上已经装了VS2010了,于是排除这个可能性.Google了一下,于是找到问题的解决方法.(尼玛,最近老是要去服务器上安装和部署各种东西然后就遇到各种问题\("▔□▔)/) 1.问题表现如下,弹出来一提示框:     2.根据提示,先打开角色管理工具.“控制面板”-&g