将日期时间从 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日期时间

将 C# 日期时间发送到 SQL Server - 错误“从字符串转换日期和/或时间时转换失败”

C# 中的日期时间与 SQL Server 中的 SQL 和 GETDATE()

从 c# 表单应用程序发布到 api 时,我无法将数据发送到日期时间

如何使用 C# 仅将日期(不包括时间)插入 ms 访问数据库