微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

实体框架MariaDb日期为字符串问题

如何解决实体框架MariaDb日期为字符串问题

我似乎对EF核心和我的MariaDb数据库有问题。首先,我无法采取明显行动来稍微更改数据库,因此该选项不在表中。

我有一个表格“ Contacts”,其中保存了联系日期。此联系日期以字符串值的形式保存,仅包含日期,例如:2020-08-30(2020年8月30日)。 我对此有一个EF核心映射:

entity.Property(e => e.ContactDate)
                    .Isrequired()
                    .HasColumnName("contactDate")
                    .HasColumnType("varchar(255)")

其中e.ContactDate是DateTime属性

当我在代码中使用该属性时,datetime会按预期工作,并包含保存在数据库中的日期。万岁!

但是,当我想在该日期时间进行查询时,就会出现问题。给出以下查询

SELECT `c7`.`contactDate`
    FROM `contacts` AS `c7`
    WHERE (`f`.`uuid` = `c7`.`fileUuid`) AND (`c7`.`numberOfContacts` > 0)
    ORDER BY `c7`.`contactDate`
    LIMIT 1) <= @__endDate_1) AND (`f`.`closingDate` IS NULL OR ((`f`.`closingDate` >= @__startDate_2)

属性startDate和endDate作为参数插入到查询中。例如,@__endDate_1='2019-12-31 00:00:00'。问题就出在这里MysqL将对contactDate db字段与该endDate的字符串值进行字符串比较。因为一个有时间字段,而另一个没有时间字段,所以如果我想做一个功能大的事情,就会遇到问题。 (SELECT "2020-02-04" >= "2020-02-04 00:00:00"返回0)。

我有没有办法:

  • 或更改他将每个查询中的contactDate转换为带有时间戳的值的EF
  • 还是将他将DateTime值转换为没有任何时间值的Date值的EF更改?

你们对第三种可能的解决方案有什么看法,我引入了新的“ Date”类作为EF的DateTime to Date字符串包装器?

谢谢!

解决方法

使用

.HasColumnType("varchar(255)")

您正在激活内置的EF Core DateTime to string value converter。它没有记录,但是使用以下格式(或类似格式)将DateTime值(绑定命令参数时)转换为string

"yyyy\\-MM\\-dd HH\\:mm\\:ss.FFFFFFF"

而您需要的是这样的格式

"yyyy\\-MM\\-dd"

可以通过configuring a custom value converter实现:

entity.Property(e => e.ContactDate)
    .HasConversion(
        value => value.ToString("yyyy\\-MM\\-dd"),dbValue => DateTime.Parse(dbValue,CultureInfo.InvariantCulture)
    )
    // the rest is the same
    .IsRequired()
    .HasColumnName("contactDate")
    .HasColumnType("varchar(255)"); // or .IsUnicode(false).HasMaxLength(255)

就是这样。现在有问题的参数将是@__endDate_1='2019-12-31',因此实现了项目符号2。

子弹1是不可能的。

创建特殊的Date类型是可能的,但并不容易,因为它需要大量的基础结构管道代码。有一些软件包可以执行此操作-NodaTime,NetTopology等。如果您感兴趣,可以查看它们的实现,但是IMHO支持(简单)自定义值类型不是EF Core优先级(当前),因此,在获得更好的支持之前,我将留在价值转换器上。

,

@IvanStoev 的答案已按要求并按预期进行。

这是一个可以正常运行的示例控制台程序,证明它可以正常工作:

using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Pomelo.EntityFrameworkCore.MySql.Storage;

namespace IssueConsoleTemplate
{
    public class Contact
    {
        public int ContactId { get; set; }
        public DateTime ContactDate { get; set; }
    }

    public class Context : DbContext
    {
        public virtual DbSet<Contact> Contacts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseMySql("server=127.0.0.1;port=3306;user=root;password=;database=So63655418",b => b
                        .ServerVersion(new ServerVersion("8.0.20-mysql")))
                .UseLoggerFactory(LoggerFactory.Create(b => b
                    .AddConsole()
                    .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Contact>(
                entity =>
                {
                    entity.Property(e => e.ContactDate)
                        .HasColumnType("varchar(255)")
                        .HasConversion(
                            v => v.ToString(@"yyyy\-MM\-dd"),v => DateTime.Parse(v,CultureInfo.InvariantCulture));

                    entity.HasData(
                        new Contact
                        {
                            ContactId = 1,ContactDate = new DateTime(2020,9,1,14,42,59,123),// <-- time will be dropped
                        });
                });
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            using var context = new Context();

            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();
            
            var dateParameter = new DateTime(2020,21,11,456); // <-- time will be dropped
            var contactWithExactDate = context.Contacts.SingleOrDefault(c => c.ContactDate == dateParameter);
            
            Debug.Assert(contactWithExactDate != null);
            Debug.Assert(contactWithExactDate.ContactDate == new DateTime(2020,1));
        }
    }
}

它将生成以下SQL语句:

warn: Microsoft.EntityFrameworkCore.Model.Validation[10400]
      Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data,this mode should only be enabled during development.

info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 3.1.8 initialized 'Context' using provider 'Pomelo.EntityFrameworkCore.MySql' with options: ServerVersion 8.0.20 MySql SensitiveDataLoggingEnabled DetailedErrorsEnabled

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (77ms) [Parameters=[],CommandType='Text',CommandTimeout='30']

      DROP DATABASE `So63655418`;

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (11ms) [Parameters=[],CommandTimeout='30']

      CREATE DATABASE `So63655418`;

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (70ms) [Parameters=[],CommandTimeout='30']

      CREATE TABLE `Contacts` (
          `ContactId` int NOT NULL AUTO_INCREMENT,`ContactDate` varchar(255) NOT NULL,CONSTRAINT `PK_Contacts` PRIMARY KEY (`ContactId`)
      );

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (11ms) [Parameters=[],CommandTimeout='30']

      INSERT INTO `Contacts` (`ContactId`,`ContactDate`)
      VALUES (1,'2020-09-01');

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (8ms) [Parameters=[@__dateParameter_0='2020-09-01' (Nullable = false) (Size = 255)],CommandTimeout='30']

      SELECT `c`.`ContactId`,`c`.`ContactDate`
      FROM `Contacts` AS `c`
      WHERE `c`.`ContactDate` = @__dateParameter_0
      LIMIT 2

即使不显式使用C#代码中的时间值,该程序也无需断言地执行,并且记录的SQL语句正确地将使用的参数显示为@__dateParameter_0='2020-09-01'

,

当我等待很长的答案时,我继续尝试使用Date类型的方法,并进行了具体的转换。作为一个奇迹,突然之间,EF将所有存储的字符串日期转换为查询中的datetime对象。太棒了,它做到了!

这是我的日期对象的代码:

public struct Date: IComparable<Date>,IComparable<DateTime>,IComparable,IEquatable<Date>
    {
        
        /// <summary>
        /// Turns a DateTime into a Date
        /// </summary>
        /// <param name="dateTime"></param>
        public Date(DateTime dateTime)
        {
            _date = dateTime.Date;
        }

        private readonly DateTime _date;

        public static implicit operator Date(DateTime value) => new Date(value);
        public static implicit operator DateTime(Date value) => value._date;

        public int CompareTo(Date other) => _date.CompareTo(other._date);
        public int CompareTo(DateTime other) => _date.CompareTo(new Date(other));
        public int CompareTo(object obj) => _date.CompareTo(obj);
        public bool Equals(Date other) => _date.Equals(other._date);
        public override bool Equals(object obj) => obj is Date other && Equals(other);
        public override int GetHashCode() => _date.GetHashCode();
        
    }

EF的转换如下:

public static PropertyBuilder<Date?> HasDateConversion(this PropertyBuilder<Date?> propertyBuilder) =>
            propertyBuilder
                .HasConversion(date => 
                        date.HasValue
                        ? ParseDate(date.Value)
                        : null,str =>
                        string.IsNullOrWhiteSpace(str) 
                            ? null
                            : (Date?)ParseString(str));

这导致所有日期都被选择为:

SELECT * FROM `contacts` AS `c`
WHERE CAST(`c`.`contactDate` AS datetime(6)) < '2020-01-03 05:01:03.000000'
LIMIT 1

因此,我的问题已解决:)万岁!

,

在您编写该格式时,我会将其保留在数据库中。但是我会更改查询。在这种情况下,在该线程中显示的是日期格式的转换:

MySQL compare DATE string with string from DATETIME field

SELECT * FROM contacts WHERE DATE(startTime) > '2020-08-30'

...或MariaDB的官方文档中: DATETIME Syntax

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。