如何解决实体框架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 举报,一经查实,本站将立刻删除。