ASP.NET开发遇CS0266类型转换错误及解决过程
嘿,我来帮你梳理这个让人头疼的类型转换问题,我之前也碰到过类似的情况,给你一步步拆解:
首先还原你遇到的场景:你在开发ASP.NET应用的TagsController时,碰到了这个错误:
Error CS0266
无法将类型'System.Linq.IQueryable'隐式转换为'Microsoft.EntityFrameworkCore.Query.IIncludableQueryable',存在显式转换(是否缺少强制转换) urlapp12 C:\Users\mjkpt\source\repos\teststage\urlapp12\urlapp12\Controllers\TagsController.cs 37 active
出错的控制器代码片段是这样的:
public async Task<IActionResult> Index(int id, [Bind("Urlid,Userid,UrlStr,Title")] Url blog, int Urlid) { var blogging02Context = _context.Tag.Include(t => t.Blog); if (!string.IsNullOrEmpty(Urlid.ToString())) { blogging02Context = blogging02Context.Where(t => t.Urlid == Urlid); } ViewBag.Urlid = Urlid; return View(await blogging02Context.ToListAsync()); }
对应的Url和Tag模型代码如下:
Url模型(Url.cs)
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace urlapp12.Models { public partial class Url { public Url() { Post = new HashSet<Post>(); } [Key] public int Urlid { get; set; } public string UserId { get; set; } public string UrlStr { get; set; } public string Title { get; set; } public string CreatedBy { get; set; } public string CreatedBy_UserName { get; set; } [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")] public DateTime CreatedAt_UtcDt { get; set; } [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")] public DateTime CreatedAt_LocalDt { get; set; } public string LastUpdatedBy { get; set; } public string LastUpdatedBy_UserName { get; set; } [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")] public DateTime LastUpdatedAt_UtcDt { get; set; } [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")] public DateTime LastUpdatedAt_LocalDt { get; set; } public virtual ICollection<Tag> Tag { get; set; } public virtual ICollection<BlogIUDSqlHistory> BlogIUDSqlHistory { get; set; } public ICollection<Post> Post { get; set; } } }
Tag模型(Tag.cs)
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; namespace urlapp12.Models { public class Tag { [Key] public int TagId { get; set; } public int DispOrderNbr { get; set; } public string tagItem { get; set; } public int Urlid { get; set; } public string CreatedBy { get; set; } public string CreatedBy_UserName { get; set; } [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")] public DateTime CreatedAt_UtcDt { get; set; } [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")] public DateTime CreatedAt_LocalDt { get; set; } public string LastUpdatedBy { get; set; } public string LastUpdatedBy_UserName { get; set; } [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")] public DateTime LastUpdatedAt_UtcDt { get; set; } [System.ComponentModel.DataAnnotations.Schema.Column(TypeName = "datetime2")] public DateTime LastUpdatedAt_LocalDt { get; set; } [System.ComponentModel.DataAnnotations.Schema.ForeignKey("Urlid")] public Url Blog { get; set; } } }
之后你尝试了Mohsin Mehmood的代码,又碰到了新的CS0266错误:
Error CS0266 无法隐式转换类型'System.Linq.IQueryable'为'Microsoft.EntityFrameworkCore.DbSet',存在显式转换(是否缺少强制转换);无法隐式转换类型'Microsoft.EntityFrameworkCore.Query.IIncludableQueryable'为'Microsoft.EntityFrameworkCore.DbSet',存在显式转换(是否缺少强制转换)
最后调整了user1672994的代码才解决问题,下面给你讲清楚错误根源和正确修改方式:
错误根源分析
第一个错误的核心原因:
执行_context.Tag.Include(t => t.Blog)时,EF Core返回的是IIncludableQueryable<Tag, Url>类型(这是IQueryable<Tag>的子类,专门标记已包含关联实体的查询)。但调用.Where(t => t.Urlid == Urlid)后,返回的是基础的IQueryable<Tag>类型。因为你用var声明变量,编译器会把blogging02Context的类型推断为IIncludableQueryable<Tag, Url>,后续把IQueryable<Tag>赋值给它就会出现类型不兼容的问题。第二个错误的核心原因:
如果你把blogging02Context显式声明为DbSet<Tag>,那不管是Include还是Where返回的结果都不是DbSet类型——DbSet是实体集合,而Include/Where返回的是查询对象,两者本质不同,自然会出现转换错误。
正确的解决方案
最简单的办法就是把变量blogging02Context显式声明为IQueryable<Tag>,因为IIncludableQueryable继承自IQueryable,向上转型完全安全,这样后续的Where结果也能正常赋值。
修改后的控制器代码如下:
public async Task<IActionResult> Index(int id, [Bind("Urlid,Userid,UrlStr,Title")] Url blog, int Urlid) { // 显式声明为IQueryable<Tag>,兼容Include和Where的返回类型 IQueryable<Tag> blogging02Context = _context.Tag.Include(t => t.Blog); if (!string.IsNullOrEmpty(Urlid.ToString())) { blogging02Context = blogging02Context.Where(t => t.Urlid == Urlid); } ViewBag.Urlid = Urlid; return View(await blogging02Context.ToListAsync()); }
另外,也可以用链式调用的方式直接构建查询,避免变量类型冲突:
public async Task<IActionResult> Index(int id, [Bind("Urlid,Userid,UrlStr,Title")] Url blog, int Urlid) { var query = _context.Tag.Include(t => t.Blog); if (!string.IsNullOrEmpty(Urlid.ToString())) { query = query.Where(t => t.Urlid == Urlid); } ViewBag.Urlid = Urlid; return View(await query.ToListAsync()); }
这样修改后,类型转换的问题就彻底解决了,代码也能正常运行。
内容的提问来源于stack exchange,提问作者Kou

