Драйверы DevArt MySql EF6: для столбцов с нулевым значением BIT (1) строки с нулевым значением всегда оцениваются как истинные для 'COLUMN_NAME ?? ложный'

Используемые библиотеки:

  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

Мы ориентируемся на .net4.7.1 из проекта ASP.NET MVC5. Наши репозитории построены на основе EF .edmx (подход db-first). Наши запросы выглядят так:

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

NFS_SUBFILTER_YN объявлен как логическое значение в нашем .edmx, также известном как:

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

Ddl NB_FILTERS выглядит так:

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

В таком сценарии, если некоторые строки имеют значение NULL, данное выражение linq возвращает «истина» для всех из них вместо «ложь». Похоже, что виноват в автоматически сгенерированном sql:

  actual => CASE WHEN Extent1.NFS_SUBFILTER_YN IS NULL THEN 0 ELSE Extent1.NFS_SUBFILTER_YN END

  corrected => CASE WHEN Extent1.NFS_SUBFILTER_YN IS NULL THEN 1 ELSE CAST(Extent1.NFS_SUBFILTER_YN AS SIGNED) END

Как можно обойти эту ошибку без массового изменения базовых таблиц и / или самих операторов linq до тех пор, пока Devart не выпустит фактическое исправление?

Примечания:

  • Для тех, кто заинтересован, мы уведомили разработчиков devart об этой проблеме в надежде, что она будет решена в какой-то момент:

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

  • Достаточно интересно, что такие запросы работают, когда задействованные столбцы являются столбцами с десятичным числом (x, y), допускающими значение NULL [автоматически сгенерированный sql правильно использует CAST (... AS SIGNED)]. Похоже, что каким-то образом бит (x) был пропущен из списка типов, которые подходят для такой обработки.

зачем разрешать нулевое значение, если это бит (1) тип? Используя / разрешая null, вы разрешаете (3) истинные, ложные, неизвестные значения, когда вашему приложению требуется только (2) истинное / ложное. Я бы посоветовал изменить ваш столбец и установить его как ненулевое, значение по умолчанию = false. Вам не нужно иметь дело с лишними хлопотами.

Krish 28.03.2018 13:11

Хороший аргумент. К сожалению, моя команда не может настроить базу данных на данный момент как из-за временных ограничений, так и из-за того, что поломка может быть вызвана в других частях / платформах, поражающих тот же БД. Еще один типичный день в мире дев-лэндов :(

XDS 28.03.2018 13:26

Ваша проблема и решение ниже напоминают мне первый день в моем универе и "историю качелей деревьев" :) :)

Krish 28.03.2018 13:29

Что делать, если вы делаете IsHiddenSubFilter = (bool?)x.NFS_SUBFILTER_YN ?? false?

Gert Arnold 28.03.2018 13:32

@GertArnold x.NFS_SUBFILTER_YN уже объявлен как Nullable <bool>, поэтому приведение не имеет никакого эффекта

XDS 28.03.2018 14:49

Конечно, но это может удачно повлиять на сгенерированный SQL.

Gert Arnold 28.03.2018 16:18

@GertArnold Я тоже тестировал, но, к сожалению, без радости

XDS 28.03.2018 17:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В качестве временного временного решения мы решили использовать перехватчик EF6 в mysql, чтобы обнаруживать и исправлять операторы 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>

Другие вопросы по теме