DevArt MySql EF6驱动程序:Nullable BIT(1)列的空值行总是被评估为'COLUMN_NAME?假'

时间:2018-03-28 11:04:14

标签: entity-framework linq-to-sql entity-framework-6

自由人雇用:

then

我们的目标是从ASP.NET MVC5项目中定位.net4.7.1。我们的存储库建立在EF .edmx(db-first方法)之上。我们的查询如下:

  Devart.Data.dll => 5.0.1878.0
  Devart.Data.MySql.dll => 8.10.1086.0
  Devart.Data.MySql.Design.dll => 8.10.1086.0
  Devart.Data.MySql.Entity.EF6.dll => 8.10.1086.0
  EntityFramework => 6.2.0

NFS_SUBFILTER_YN在我们的.edmx中声明为布尔值:

  var results = _db.NB_FILTERS
        .Select(x => new ReportFiltersDTO { IsHiddenSubFilter = x.NFS_SUBFILTER_YN ?? false })
        .ToList();

NB_FILTERS ddl是这样的:

  <Property Name="NFS_SUBFILTER_YN" Type="boolean" />

在这种情况下,如果某些行为null,则给定的linq表达式将为所有行返回“true”而不是“false”。罪魁祸首似乎是在自动生成的sql:

  CREATE TABLE NB_FILTERS (
        [...]
        NFS_SUBFILTER_YN BIT(1) NULL,
        [...]
  )

在Devart推出实际修复程序之前,如何在不更改基础表和/或linq语句本身的情况下解决此错误?

图片的标题说明:

  • 对于那些感兴趣的人,我们已经向devart开发者通知了这个问题,希望能在某个时候解决这个问题:

    https://forums.devart.com/viewtopic.php?f=2&t=36955

  • 有趣的是,当涉及的列是可以为空的十进制(x,y)列时,这些查询会起作用[自动生成的sql正确使用CAST(... AS SIGNED)]。似乎在某些类型列表中省略了bit(x),这些类型有资格进行此类处理。

1 个答案:

答案 0 :(得分:0)

作为一种临时的止损解决方案,我们选择在mysql上使用EF6拦截器来检测和纠正针对布尔列的动态sql语句(幸运的是,如果不是所有的布尔列都具有_YN后缀)这有很大帮助):

using System.Data;
using System.Data.Common;
using System.Data.Entity.Infrastructure.Interception;
using System.Text.RegularExpressions;

namespace Some.Namespace
{
    //to future maintainers     the devart mysql drivers for ef6 ver8.10.1086.0 suffer from what appears to be a very annoying b.ug which cause nullable bit(1) columns set to null
    //to future maintainers     to always be materialized to true when we evaluate them like so
    //to future maintainers
    //to future maintainers                                                  x.SOME_BOOLEAN_COLUMN ?? false
    //to future maintainers
    //to future maintainers     this is obviously flat out wrong and to remedy this issue we intercept the offending sql snippets and hotfix them like so
    //to future maintainers    
    //to future maintainers                before => CASE WHEN Extent1.NFS_SUBFILTER_YN    IS NULL THEN 1 ELSE             Extent1.NFS_SUBFILTER_YN            END
    //to future maintainers                after  => CASE WHEN Extent1.NFS_SUBFILTER_YN    IS NULL THEN 1 ELSE     CAST(Extent1.NFS_SUBFILTER_YN AS SIGNED)    END
    //to future maintainers
    public sealed class MySqlHotFixerCommandInterceptor : IDbCommandInterceptor
    {
        //beforeexecution
        public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) => LogWarningBeforeExecutionIfSynchronousExecutionIsFound(command, interceptionContext);
        public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) => LogWarningBeforeExecutionIfSynchronousExecutionIsFound(command, interceptionContext);
        public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) => LogWarningBeforeExecutionIfSynchronousExecutionIsFound(command, interceptionContext);

        //afterexecution
        public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) {}
        public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) {}
        public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) {}

        static private void LogWarningBeforeExecutionIfSynchronousExecutionIsFound<TResult>(IDbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
        {
            if (interceptionContext.Exception != null)
                return;

            command.CommandText = FaultySwitchCaseSpotter1.Replace(command.CommandText, "CAST($3 AS SIGNED)");
            command.CommandText = FaultySwitchCaseSpotter2.Replace(command.CommandText, "CAST($3 AS SIGNED)");
        }

        private static readonly Regex FaultySwitchCaseSpotter1 = new Regex(@"(?<=CASE\s+WHEN\s+(`?[a-zA-Z0-9_]+?`?[.])?`?[a-zA-Z0-9_]+?`?\s+IS\s+NULL\s+THEN\s+(0|1)\s+ELSE\s+)((`?[a-zA-Z0-9_]+?`?[.])?`?[a-zA-Z0-9_]+?`?)(?=\s*END)", RegexOptions.IgnoreCase);
        private static readonly Regex FaultySwitchCaseSpotter2 = new Regex(@"(?<=CASE\s+WHEN\s+(`?[a-zA-Z0-9_]+?`?[.])?`?[a-zA-Z0-9_]+?_YN`?\s+IS\s+NULL\s+THEN\s+(\d+|.[a-zA-Z0-9_]+?|`?[a-zA-Z0-9_]+?`?[.]`?[a-zA-Z0-9_]+?`?)\s+ELSE\s+)((`?[a-zA-Z0-9_]+?`?[.])?`?[a-zA-Z0-9_]+?_YN`?)(?=\s*END)", RegexOptions.IgnoreCase);
    }
}

web.config / app.config也需要调整,如下所示:

<configuration>

  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
       [...]
    </defaultConnectionFactory>

    <providers>
       [...]
    </providers>

    <interceptors>
      <!-- keep first -->
      <interceptor type="Some.Namespace.MySqlHotFixerCommandInterceptor, Organotiki.Infrastructure.Core">
      </interceptor>
      [...]
    </interceptors>
  </entityFramework>
</configuration>