热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

记一次EFCore类型转换错误及解决方案

这篇文章主要介绍了记一次EFCore类型转换错误及解决方案,帮助大家更好的理解和学习使用asp.netcore,感兴趣的朋友可以了解下

一  背景

  今天在使用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 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(queryResult);
 result.Details = _objectMapper.Map>(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 _productRepository;
 private readonly IRepository _vehicleInformationRepository;

   上面是局部变量的定义,在我们的示例代码中我们使用ABP框架来作为整个项目代码的基础框架,这里的IRepository接口来自于ABP框架中定义的接口类型用于直接操作数据库表,这里具体的实现就是通过构造函数来进行注入的,具体请参考下面的实例。

public DegradedVehicleContractService(IObjectMapper objectMapper,
 IRepository degradedVehicleContractRepository,
 IRepository productRepository,
 IRepository vehicleInformationRepository,
 IRepository companyRepository,
 IDegradedVehicleContractManager degradedVehicleContractManager,
 IRepository productAffiProductCategoryRepository,
 IRepository productCategoryBusinessDomainRepository,
 IRepository tiledProductCategoryRepository,
 IRepository 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被定义成了varchar(50)类型,明显在和代码中guid类型进行匹配的时候会发生错误,后来我很疑惑我们的开发模式是采用Code First来进行开发的,现有实体然后再通过Migration进行数据库迁移的,应该不会出现这样的错误,事后得知是另外一位同事在开发的过程中手动去更改了这个实体的类型从而导致了这个问题,最后更改数据库UnionId字段类型,然后发现错误消失,至此问题解决。

总结

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

以上就是记一次EFCore类型转换错误及解决方案的详细内容,更多关于EFCore类型转换错误及解决方案的资料请关注其它相关文章!


推荐阅读
  • 本文介绍了使用postman进行接口测试的方法,以测试用户管理模块为例。首先需要下载并安装postman,然后创建基本的请求并填写用户名密码进行登录测试。接下来可以进行用户查询和新增的测试。在新增时,可以进行异常测试,包括用户名超长和输入特殊字符的情况。通过测试发现后台没有对参数长度和特殊字符进行检查和过滤。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 延迟注入工具(python)的SQL脚本
    本文介绍了一个延迟注入工具(python)的SQL脚本,包括使用urllib2、time、socket、threading、requests等模块实现延迟注入的方法。该工具可以通过构造特定的URL来进行注入测试,并通过延迟时间来判断注入是否成功。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 本文介绍了一个免费的asp.net控件,该控件具备数据显示、录入、更新、删除等功能。它比datagrid更易用、更实用,同时具备多种功能,例如属性设置、数据排序、字段类型格式化显示、密码字段支持、图像字段上传和生成缩略图等。此外,它还提供了数据验证、日期选择器、数字选择器等功能,以及防止注入攻击、非本页提交和自动分页技术等安全性和性能优化功能。最后,该控件还支持字段值合计和数据导出功能。总之,该控件功能强大且免费,适用于asp.net开发。 ... [详细]
  • 背景应用安全领域,各类攻击长久以来都危害着互联网上的应用,在web应用安全风险中,各类注入、跨站等攻击仍然占据着较前的位置。WAF(Web应用防火墙)正是为防御和阻断这类攻击而存在 ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • 本文介绍了互联网思维中的三个段子,涵盖了餐饮行业、淘品牌和创业企业的案例。通过这些案例,探讨了互联网思维的九大分类和十九条法则。其中包括雕爷牛腩餐厅的成功经验,三只松鼠淘品牌的包装策略以及一家创业企业的销售额增长情况。这些案例展示了互联网思维在不同领域的应用和成功之道。 ... [详细]
  • 本文详细介绍了Mybatis中#与$的区别及其作用。#{}可以防止sql注入,拼装sql时会自动添加单引号,适用于单个简单类型的形参。${}则将拿到的值直接拼装进sql,可能会产生sql注入问题,需要手动添加单引号,适用于动态传入表名或字段名。#{}可以实现preparedStatement向占位符中设置值,自动进行类型转换,有效防止sql注入,提高系统安全性。 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
  • wpf+mvvm代码组织结构及实现方式
    本文介绍了wpf+mvvm代码组织结构的由来和实现方式。作者回顾了自己大学时期接触wpf开发和mvvm模式的经历,认为mvvm模式使得开发更加专注于业务且高效。与此同时,作者指出mvvm模式相较于mvc模式的优势。文章还提到了当没有mvvm时处理数据和UI交互的例子,以及前后端分离和组件化的概念。作者希望能够只关注原始数据结构,将数据交给UI自行改变,从而解放劳动力,避免加班。 ... [详细]
  • 数据库锁的分类和应用
    本文介绍了数据库锁的分类和应用,包括并发控制中的读-读、写-写、读-写/写-读操作的问题,以及不同的锁类型和粒度分类。同时还介绍了死锁的产生和避免方法,并详细解释了MVCC的原理以及如何解决幻读的问题。最后,给出了一些使用数据库锁的实际场景和建议。 ... [详细]
  • Django + Ansible 主机管理(有源码)
    本文给大家介绍如何利用DjangoAnsible进行Web项目管理。Django介绍一个可以使Web开发工作愉快并且高效的Web开发框架,能够以最小的代价构建和维护高 ... [详细]
author-avatar
chenkaij_305
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有