一个编译的BUG,在原生的VS2015中没有问题,但更新至VS2015 Update1之后就有了,大概是Update1用了新的编译器,害得我好苦。经测试,VS2013 Update5中也存在这个问题,大概是用了相同的编译器。

  下面我们来重现这个BUG,用最简洁的代码来重现这个BUG。

测试代码

一个接口

一个很简单的接口,定义一个“是否锁定”的功能

     public interface ILockable
{
bool IsLocked { get; set; }
}

一个实体

同样一个很简单的实体定义,实现了上面定义的 ILockable 接口

     public class TestModel : ILockable
{
public int Id { get; set; } public string Name { get; set; } public bool IsLocked { get; set; }
}

EF上下文类

EF上下文类,同样极其简单

     public class TestDbContext : DbContext
{
public TestDbContext()
: base("default")
{ } public DbSet<TestModel> TestModels { get; set; }
}

接口的扩展方法

为了方便查询所有未锁定的数据,给实现了 ILockable 接口的实体定义了一个查询的IQueryable<T>扩展方法,代码同样很简单

     public static class Extensions
{
public static IQueryable<TEntity> Unlocked<TEntity>(this IQueryable<TEntity> source)
where TEntity : ILockable
{
return source.Where(m => !m.IsLocked);
}
}

执行查询代码

下面,使用上面的代码来做一个很简单的查询

     internal class Program
{
private static void Main(string[] args)
{
try
{
var db = new TestDbContext();
var models = db.TestModels.Unlocked().ToList();
Console.WriteLine(models.Count);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadLine();
}
}

至此,所有代码展现完毕,就是这么简单。

异常展示

代码的意思也很明白,也没有什么明显的问题,但在VS2015 Update1中,会报这么个异常:

"无法将类型“RoslynBugShow.TestModel”强制转换为类型“RoslynBugShow.ILockable”。LINQ to Entities 仅支持强制转换 EDM 基元或枚举类型。"}
Data: {System.Collections.ListDictionaryInternal}
HResult: -
HelpLink: null
InnerException: null
Message: "无法将类型“RoslynBugShow.TestModel”强制转换为类型“RoslynBugShow.ILockable”。LINQ to Entities 仅支持强制转换 EDM 基元或枚举类型。"
Source: "EntityFramework"
StackTrace: " 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.ValidateAndAdjustCastTypes(TypeUsage toType, TypeUsage fromType, Type toClrType, Type fromClrType)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.GetCastTargetType(TypeUsage fromType, Type toClrType, Type fromClrType, Boolean preserveCastForDateTime)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.CreateCastExpression(DbExpression source, Type toClrType, Type fromClrType)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.ConvertTranslator.TranslateUnary(ExpressionConverter parent, UnaryExpression unary, DbExpression operand)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.UnaryTranslator.TypedTranslate(ExpressionConverter parent, UnaryExpression linq)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Translate
Expression(Expression linq)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MemberAccessTranslator.TypedTranslate(ExpressionConverter parent, MemberExpression linq)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`.Translate(ExpressionConverter parent, Expression linq)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.NotTranslator.TypedTranslate(ExpressionConverter parent, UnaryExpression linq)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`.Translate(ExpressionConverter parent, Expression linq)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(Lamb
daExpression lambda, DbExpression input, DbExpressionBinding& binding)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`.Translate(ExpressionConverter parent, Expression linq)\r\n 在 System.Data.Entity
.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)\r\n 在 System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()\r\n 在 System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable` forMergeOption)\r\n 在 System.Data.Entity.Core.Objects.ObjectQuery`.<>c__DisplayClass7.<GetResults>b__6()\r\n 在 System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func` func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)\r\n 在 System.Data.Entity.Core.Objects.ObjectQuery`.<>c__DisplayClass7.<GetResults>b__5()\r\n 在 System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func` operation)\r\n 在 System.Data.Entity.Core.Objects.ObjectQuery`.GetResults(Nullable` forMergeOption)\r\n 在 System.Data.Entity.Core.Objects.ObjectQuery`.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()\r\n 在 System.Data.Entity.Internal.LazyEnumerator`.MoveNext()\r\n 在 Sy
stem.Collections.Generic.List`..ctor(IEnumerable` collection)\r\n 在 System.Linq.Enumerable.ToList[TSource](IEnumerable` source)\r\n 在 RoslynBugShow.Program.Main(String[] args) 位置 d:\\documents\\visual studio \\Projects\\RoslynBugShow\\RoslynBugShow\\Program.cs:行号 "
TargetSite: {System.Data.Entity.Core.Metadata.Edm.TypeUsage ValidateAndAdjustCastTypes(System.Data.Entity.Core.Metadata.Edm.TypeUsage, System.Data.Entity.Core.Metadata.Edm.TypeUsage, System.Type, System.Type)

BUG分析

看到这,估计能看懂的各位也蒙圈了,代码看起来真的没问题,怎么会出问题了呢,多出的这个强制转换是怎么回事。

反正,我是在这当中纠结到吐血,一度以为是实体设计的问题(在实际项目中,实体设计当然要比上面这个要复杂得多得多,排查问题的难度要大得多),后来,无意中查看到了反编译的代码,才揪出了罪魁祸首,下面来对比VS2015 Update1与VS2015两个编译器对上面的 Unlocked 扩展方法的编译:

原生 VS2015 编译

VS2015 Update1编译

由上面的反编译代码可看到,VS2015 Update1的编译结果多了个强制转换,但这种强制转换在Linq To Entities中是无法支持的,因而报了

无法将类型“RoslynBugShow.TestModel”强制转换为类型“RoslynBugShow.ILockable”。LINQ to Entities 仅支持强制转换 EDM 基元或枚举类型。

这个异常。

问题上升到编译器的高度了,已经不是我们这些平民小码农能解决的了,只能期待微软在VS2015的下一个更新中能解决掉。

解决方案

果然还是园子中牛人多,29楼的@Choo 提供的一个解决方案完美解决了这个问题

扩展方法的泛型类型添加一个 class 限制,VS2015 Update1 与VS2015编译的结果完全一致,问题圆满解决!

演示代码下载:RoslynBugShow.rar

爆一个VS2015 Update1更新带来的编译BUG【已有解决方案】的更多相关文章

  1. Windows 10开发基础——VS2015 Update1新建UWP项目,XAML设计器无法加载的解决

    这次,我们来解决一个问题...在使用Visual Studio 2015 Update 1的时候,新建一个UWP的项目,XAML设计器就会崩,具体异常信息如下图: 解决方法如下:下面圈出的那个路径就按 ...

  2. ASP.NET网站发布-允许更新此预编译站点 打勾与不打勾的区别

    发布网站时在打开的对话框中,有一个选项是至关重要的,那就是“允许更新此预编译站点”: “允许更新此预编译站点”这一项,默认情况下,前面是打上一个√的,至于要不要打上一个√,是可选的,那么,打勾与不打勾 ...

  3. net网站发布-允许更新此预编译站点 及修改发布后内容

    我们可以通过如下的方法发布VS2010的网站: “生成”→“发布网站”:弹出对话框! 在打开的对话框中,有一个选项是至关重要的,那就是“允许更新此预编译站点”: “允许更新此预编译站点”这一项,默认情 ...

  4. ASP.NET Web项目发布选项:“允许更新此预编译站点” 详解

    目录 #使用visual studio 发布web项目 #"允许更新此预编译站点" 选项的意义 1.选中 "允许更新此预编译站点" 2.不选中 "允许 ...

  5. Delphi函数翻译成VC要注意句柄指针传递(传递Handle的时候,必须加上一个指针引用,才能消除编译错误)

    Delphi里做了魔法变化,每个变量名称本身就是指针,因为不怎么需要指针语法.我也不知道是不是因为这个原因引起的Delphi与VC对句柄的不同处理. 这是Delphi的强行关机函数,好用,调用方式:W ...

  6. 创建预编译头 Debug 正常 Release Link Error:预编译头已存在,使用第一个 PCH

    创建预编译头 Debug 正常 Release Link Error Main.obj : error LNK2005: ___@@_PchSym_@00@UmfilkilqUdrmzkkUkilqU ...

  7. 给大家推荐一个.Net的混淆防反编译工具ConfuserEx

    给大家推荐一个.Net的混淆防反编译工具ConfuserEx. 由于项目中要用到.Net的混淆防反编译工具. 在网上找了很多.Net混淆或混淆防反编译工具,如.NET Reactor.Dotfusca ...

  8. Chromium CEF 2623 -- 支持 xp 的最后一个版本源码下载和编译步骤

    背景 因为项目需要在客户端中内嵌浏览器,需要支持 xp 操作系统和播放视频,但 CEF 2623 以后的版本已经不支持 xp 操作系统,也不再提供 2623 版本的二进制发布包下载,只好自己手动编译. ...

  9. android为什么不允许新开启一个线程来更新UI,而是用handler来更新界面

    下面是快速创建一个新线程的方法: 第一种:直接创建子线程并启动      new Thread() {@Overridepublic void run() {     //这里写入子线程需要做的工作  ...

随机推荐

  1. PO,VO,BO,DTO,POJO(POCO),DAO的区别(转载)

    PO:persistant object持久对象 最形象的理解就是一个PO就是数据库中的一条记录.好处是可以把一条记录作为一个对象处理,可以方便的转为其它对象. BO:business object业 ...

  2. FTP命令 - Size的问题

    今天发现一个服务从某一个外接系统(Linux)FTP取到的文件大小和下载后的文件大小总是不一致. 开始以为是程序那里出错了.但是找来找去发现不了原因.后来用FTP工具上去执行SIZE命令,终于发现返回 ...

  3. Python之路第一课Day4--随堂笔记(迭代生成装饰器)

    上节回顾: 1.集合 a.关系测试 b.去重 2.文件操作及编码 3.函数 4.局部变量和全局变量 上节回顾 本节课内容: 1.迭代器生成器 2.装饰器 3.json pickle数据序列化 4.软件 ...

  4. ARM 编译 phddns

    参考博文http://bluegemyf.blog.163.com/blog/static/11816340201310472751513/ 1.安装必要的开发包 sudo apt-get  inst ...

  5. Android_SQLite数据库增删改查操作

    一:什么是SQLite? 在Android平台上,集成了一个嵌入式关系型轻量级的数据库. 二:什么时候用的数据库? 有大量相似机构的数据需要存储时. 三:如何创建一个数据库? 1.创建一个Sqlite ...

  6. dom初识

    1什么是dom document object model文档对象模型 是将整个页面文档封装成了一个对象,就是一个文档对象 整个页面就是一个文档,是由很多的节点组成的节点又包括三部分: 元素 属性 文 ...

  7. MSDN杂志上Wix相关的文章

    使用 MSBuild 和 Windows Installer XML 执行自动发布: http://msdn.microsoft.com/zh-cn/magazine/cc163456.aspx 用于 ...

  8. Lesson 2 Breakfast or lunch?

    Text It was Sunday. I never get up early on Sundays. I sometimes stay in bed until lunchtime. Last S ...

  9. 我的权限系统设计实现MVC4 + WebAPI + EasyUI + Knockout(一)

    一.前言 之前的博客一直都还没写到框架的实现及权限系统,今天开始写我的权限系统,我以前做过的项目基本上都有权限管理这个模块,但各个系统都会有一些不太一样,有些简单点,有些稍微复杂一点,一句话,我们做的 ...

  10. CSS调试小技巧 —— 调试DOM元素hover,focus,actived的样式

    最近学习html5和一些UI框架,接触css比较多,就来跟大家分享一下css中的一些调试技巧.之前做页面,css都是自己写的,所以要改哪里可以很快的找到,现在使用了UI框架,里面的样式是不可能读完的, ...