简体   繁体   English

将日期时间从 C# 插入到 SqlSrv

[英]Insert datetime from C# to SqlSrv

I need to save in the database a date field type "datetime" from C# using EntityFramework.我需要使用 EntityFramework 在数据库中保存来自 C# 的日期字段类型“datetime”。

In MySql works fine but no in Sqlsrv.在 MySql 中工作正常但在 Sqlsrv 中没有。

The idea is that the system can work with MySql or SqlSrv, that's why I use datetime and not datetime2.这个想法是系统可以使用 MySql 或 SqlSrv,这就是我使用 datetime 而不是 datetime2 的原因。

My model:我的model:

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; }
}

My 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();
    }

My Controller:我的Controller:

var entity = new Entity
{
    Name = installer.CompanyName,
    ...
};
_context.Entities.Add(entity);
await _context.SaveChangesAsync();

Exception:例外:

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)

I tried setting the fields to nullable, but instead of throwing an exception it doesn't save any date.我尝试将字段设置为可为空,但它没有抛出异常,而是不保存任何日期。

The problem is that the default value for a DateTime instance in .NET is 0001-01-01T00:00:00 (ISO8601 notation).问题是 .NET 中DateTime实例的默认值是0001-01-01T00:00:00 (ISO8601 表示法)。 In Sql Server the range of a DateTime is limited and must be greater than 1753-1-1T00:00:00 (also ISO8601 notation).在 Sql 服务器中,日期时间的范围是有限的,必须大于1753-1-1T00:00:00 (也是 ISO8601 表示法)。 You must specify a value of your DateTime instance that is greater than this date.您必须指定一个大于此日期的DateTime实例值。

An alternative is to use a Nullable DateTime?另一种方法是使用 Nullable DateTime? instance and make the column value accept nulls in your DB schema so that a lack of value results in a NULL value in the database.实例并使列值在您的数据库架构中接受空值,这样缺少值会导致数据库中出现 NULL 值。

If you are sure that you are setting the values correctly before the save you should profile the operation using Sql Profiler and see what the value is that is being passed and causing the Exception.如果您确定在保存之前正确设置了值,您应该使用 Sql Profiler 分析操作并查看正在传递并导致异常的值是什么。 It could be that the name of the column does not correspond with the name of the schema column or that there is another column in the database that is not mapped back to your entity.可能是列的名称与模式列的名称不对应,或者数据库中有另一个列未映射回您的实体。

I do something similar in my projects.我在我的项目中做了类似的事情。 Below is the code I have and it is working perfectly fine in over 10 projects.下面是我的代码,它在 10 多个项目中运行良好。 Below is my DbContext下面是我的 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;
            }
        }
    }

Oh I guess if you want the model for my audit fields here that is too:哦,我想如果你想要 model 作为我的审计字段,那也是:

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; }
    }

在此处输入图像描述

After many test, finally I solve my problem.经过多次测试,终于解决了我的问题。

I remove the datetime definition from DBContext and leave it in the model definition, so that EF decides which type of datetime to choose for the DB type chosen by the client.我从 DBContext 中删除日期时间定义并将其保留在 model 定义中,以便 EF 决定为客户端选择的 DB 类型选择哪种类型的日期时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM