将日期时间从 C# 插入到 SqlSrv
Posted
技术标签:
【中文标题】将日期时间从 C# 插入到 SqlSrv【英文标题】:Insert datetime from C# to SqlSrv 【发布时间】:2021-10-20 09:47:14 【问题描述】:我需要使用 EntityFramework 从 C# 中将日期字段类型“datetime”保存在数据库中。
在 mysql 中工作正常,但在 Sqlsrv 中没有。
这个想法是系统可以使用 MySql 或 SqlSrv,这就是我使用 datetime 而不是 datetime2 的原因。
我的模特:
public partial class Entity: ITimeStamp
public long Id get; set;
public string Name get; set;
public DateTime CreatedAt get; set;
public DateTime UpdatedAt get; set;
我的 DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
.......
modelBuilder.Entity<Entity>(entity =>
entity.ToTable("entities");
entity.HasIndex(e => e.Name, "entities_name_unique")
.IsUnique();
entity.Property(e => e.Id)
.HasColumnType("int")
.HasColumnName("id").ValueGeneratedOnAdd();
entity.Property(e => e.CreatedAt)
.HasColumnType("datetime")
.HasColumnName("created_at");
entity.Property(e => e.Name)
.IsRequired()
.HasColumnType("varchar(255)")
.HasColumnName("name");
entity.Property(e => e.UpdatedAt)
.HasColumnType("datetime")
.HasColumnName("updated_at");
);
......
public override int SaveChanges()
var entries = ChangeTracker
.Entries()
.Where(e => e.Entity is ITimeStamp && (
e.State is EntityState.Added or EntityState.Modified));
foreach (var entityEntry in entries)
((ITimeStamp)entityEntry.Entity).UpdatedAt = DateTime.Now;
if (entityEntry.State == EntityState.Added)
((ITimeStamp)entityEntry.Entity).CreatedAt = DateTime.Now;
return base.SaveChanges();
我的控制器:
var entity = new Entity
Name = installer.CompanyName,
...
;
_context.Entities.Add(entity);
await _context.SaveChangesAsync();
例外:
System.Data.SqlTypes.SqlTypeException: SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.
at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__169_0(Task`1 result)
at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
at System.Threading.Tasks.Task.<>c.<.cctor>b__277_0(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location ---
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
我尝试将字段设置为可为空,但它没有抛出异常,而是不保存任何日期。
【问题讨论】:
又一个想法。您的日期时间在数据库中可以为空吗?如果是这样,那么也许在 DateTime 变量的末尾做一个 .Value 可以解决这个问题。 【参考方案1】:问题在于 .NET 中 DateTime
实例的默认值是 0001-01-01T00:00:00
(ISO8601 表示法)。在 Sql Server 中,DateTime 的范围是有限的,并且必须大于 1753-1-1T00:00:00
(也是 ISO8601 表示法)。您必须为您的 DateTime
实例指定一个大于此日期的值。
另一种方法是使用 Nullable DateTime?实例并让列值在您的 DB 模式中接受空值,以便缺少值导致数据库中的 NULL 值。
如果您确定在保存之前正确设置了值,您应该使用 Sql Profiler 分析操作并查看传递的值是什么并导致异常。可能是列的名称与架构列的名称不对应,或者数据库中的另一列未映射回您的实体。
【讨论】:
正如我在帖子中提到的:我尝试将字段设置为可为空,但它没有抛出异常,而是不保存任何日期。 @xSkArx - 正如我提到的,您需要使用分析器或调试代码以查看实际传递的内容。我相信您实际上根本没有设置该值,而是使用了默认值(DateTime 的空值或默认值取决于它是否可以为空)。调试代码后,您将能够看到这一点,并希望开始理解为什么您的值没有被传递。【参考方案2】:我在我的项目中做类似的事情。以下是我拥有的代码,它在 10 多个项目中运行良好。下面是我的 DbContext
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
//This allows the audit fields to automatically be added, or updated.
public override int SaveChanges()
var state = this.ChangeTracker.Entries().Select(x => x.State).ToList();
state.ForEach(x =>
switch(x)
case EntityState.Added:
UpdateAddedEntity();
break;
case EntityState.Modified:
UpdateModifiedEntity();
break;
);
return base.SaveChanges();
//Update Entity Pattern
protected void UpdateAddedEntity()
var created = this.ChangeTracker.Entries().Where(e => e.State == EntityState.Added).Select(e => e.Entity).ToArray();
foreach (var entity in created)
if (entity is AuditFields)
var auditFields = entity as AuditFields;
auditFields.CreateDateTimeUtc = DateTime.UtcNow;
auditFields.ModifiedDateTimeUtc = DateTime.UtcNow;
auditFields.Active = true;
protected void UpdateModifiedEntity()
//Modified record changes
var modified = this.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified).Select(e => e.Entity).ToArray();
foreach (var entity in modified)
if (entity is AuditFields)
var auditFields = entity as AuditFields;
auditFields.ModifiedDateTimeUtc = DateTime.UtcNow;
哦,我猜你是否也想要我的审计字段的模型:
public class AuditFields
protected AuditFields()
Active = true;
CreateDateTimeUtc = DateTime.UtcNow;
ModifiedDateTimeUtc = DateTime.UtcNow;
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id get; set;
[Required]
[IndexColumn]
public DateTime CreateDateTimeUtc get; set;
[Required]
[IndexColumn]
public DateTime ModifiedDateTimeUtc get; set;
[IndexColumn]
public bool Active get; set;
【讨论】:
这在 sqlsrv 中对您有用吗?它只适用于我的 MySql 是的。我已经将它用于 SQL Server 和 MySQL。我在 SQL 中的 DateTime 字段是 datetime2(7)。那是你的吗?并且这些字段是不可为空的。我在 sql server 中添加了我的字段的图片 我都使用日期时间,因为用户可以选择使用哪种类型的数据库。问题是MySql中没有datetime2 正确。 Datetime2 是 MS SQL Server 中使用的术语。定义为 datetime2(6) 的 MS SQL Server 列等效于 MySQL 5.7 日期时间或时间戳列。因此您可以更改为 datetime2(6),或者您必须修改 datetime 以遵守 mysql 中的列约束【参考方案3】:经过多次测试,终于解决了我的问题。
我从 DBContext 中删除日期时间定义并将其保留在模型定义中,以便 EF 决定为客户端选择的 DB 类型选择哪种类型的日期时间。
【讨论】:
以上是关于将日期时间从 C# 插入到 SqlSrv的主要内容,如果未能解决你的问题,请参考以下文章
如何从 MySql 数据库中获取日期到 C# DateTime 对象?
将 C# 日期时间发送到 SQL Server - 错误“从字符串转换日期和/或时间时转换失败”
C# 中的日期时间与 SQL Server 中的 SQL 和 GETDATE()