web-dev-qa-db-ja.com

DbContextとSetInitializerを使用してdatetime2の範囲外の変換エラーを修正する方法は?

Entity Framework 4.1で導入されたDbContextおよびCode First APIを使用しています。

データモデルは、stringDateTimeなどの基本的なデータ型を使用します。いくつかのケースで使用しているデータアノテーションは[Required]だけですが、それはDateTimeプロパティにはありません。例:

public virtual DateTime Start { get; set; }

DbContextサブクラスもシンプルで、次のようになります。

public class EventsContext : DbContext
{
    public DbSet<Event> Events { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Event>().ToTable("Events");
    }
}

initializerは、モデルの日付を今年または来年の適切な値に設定します。

ただし、イニシャライザを実行すると、context.SaveChanges()でこのエラーが発生します。

Datetime2データ型からdatetimeデータ型への変換の結果、範囲外の値が生じました。ステートメントは終了されました。

すべてが非常に単純なので、なぜこれが起こっているのかまったくわかりません。また、編集するedmxファイルがないため、修正方法もわかりません。

何か案は?

126
Alex Angas

StartがSqlDateTime.MinValue(1753年1月1日)以上であることを確認する必要があります-デフォルトでは、StartはDateTime.MinValue(0001年1月1日)に等しくなります。

179
andygjp

シンプル。最初にコードで、DateTimeのタイプをDateTime?に設定します。そのため、データベースでnull許容のDateTime型を使用できます。エンティティの例:

public class Alarme
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        public DateTime? DataDisparado { get; set; }//.This allow you to work with nullable datetime in database.
        public DateTime? DataResolvido { get; set; }//.This allow you to work with nullable datetime in database.
        public long Latencia { get; set; }

        public bool Resolvido { get; set; }

        public int SensorId { get; set; }
        [ForeignKey("SensorId")]
        public virtual Sensor Sensor { get; set; }
    }
21
Ulisses Ottoni

場合によっては、DateTime.MinValue(または同等にdefault(DateTime))を使用して、不明な値を示します。この単純な拡張メソッドは、問題の処理に役立つはずです。

public static class DbDateHelper
{
    /// <summary>
    /// Replaces any date before 01.01.1753 with a Nullable of 
    /// DateTime with a value of null.
    /// </summary>
    /// <param name="date">Date to check</param>
    /// <returns>Input date if valid in the DB, or Null if date is 
    /// too early to be DB compatible.</returns>
    public static DateTime? ToNullIfTooEarlyForDb(this DateTime date)
    {
        return (date >= (DateTime) SqlDateTime.MinValue) ? date : (DateTime?)null;
    }
}

使用法:

 DateTime? dateToPassOnToDb = tooEarlyDate.ToNullIfTooEarlyForDb();
19
Kjartan

特定のモデリングの問題に合う場合は、フィールドをヌル可能にすることができます。 NULL日付は、デフォルト値のようにSQL DateTime型の範囲内にない日付に強制されることはありません。別のオプションは、異なるタイプに明示的にマッピングすることです。おそらく、

.HasColumnType("datetime2")
12
Brian Kretzler

この質問は非常に古く、すでに素晴らしい答えがありますが、この問題を解決するための3つの異なるアプローチを説明するものをもう1つ追加する必要があると思いました。

最初のアプローチ

テーブルの対応する列のDateTimeプロパティpublic virtual DateTime Start { get; set; }を明示的にdatetime2にマップします。 EFはデフォルトでdatetimeにマップするためです。

これは、流れるようなAPIまたはデータ注釈によって行うことができます。

  1. Fluent API

    DbContextクラスでは、OnModelCreatingをオーバーライドし、プロパティStartを構成します(説明のため、EntityClassクラスのプロパティです)。

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //Configure only one property 
        modelBuilder.Entity<EntityClass>()
            .Property(e => e.Start)
            .HasColumnType("datetime2");
    
       //or configure all DateTime Preperties globally(EF 6 and Above)
        modelBuilder.Properties<DateTime>()
            .Configure(c => c.HasColumnType("datetime2"));
    }
    
  2. データ注釈

    [Column(TypeName="datetime2")]
    public virtual DateTime Start { get; set; }
    

第2のアプローチ

StartをEntityClassコンストラクターの既定値に初期化します。これは、何らかの理由で、エンティティをデータベースの開始に保存する前にStartの値が設定されていない場合と同じです。デフォルト値が SqlDateTime.MinValue 以上であることを確認してください(1753年1月1日から9999年12月31日まで)

public class EntityClass
{
    public EntityClass()
    {
        Start= DateTime.Now;
    }
    public DateTime Start{ get; set; }
}

第3のアプローチ

StartをNULL可能型にするDateTime-noteDateTimeの後の?にする

public virtual DateTime? Start { get; set; }

詳細については、これをお読みください post

11
AIM

私の解決策は、すべてのdatetime列をdatetime2に切り替え、新しい列にdatetime2を使用することでした。つまり、デフォルトでEFがdatetime2を使用するようにします。これをコンテキストのOnModelCreatingメソッドに追加します。

modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));

それはすべてのDateTimeとDateTimeを取得しますか?すべてのエンティティのプロパティ。

8
Ogglas

データベースでDateTimeプロパティがnull可能の場合、関連付けられたオブジェクトプロパティにDateTime?を使用するか、SQL日時型が処理できる範囲外の未割り当て値にEFがDateTime.MinValueを渡すようにしてください。

7

コンストラクターでStartプロパティを初期化します

Start = DateTime.Now;

Code Firstを使用してASP .Net Identity FrameworkのUsersテーブル(AspNetUsers)にいくつかの新しいフィールドを追加しようとしたときに、これはうまくいきました。 IdentityModels.csのClass-ApplicationUserを更新し、DateTime型のlastLoginフィールドを追加しました。

public class ApplicationUser : IdentityUser
    {
        public ApplicationUser()
        {
            CreatedOn = DateTime.Now;
            LastPassUpdate = DateTime.Now;
            LastLogin = DateTime.Now;
        }
        public String FirstName { get; set; }
        public String MiddleName { get; set; }
        public String LastName { get; set; }
        public String EmailId { get; set; }
        public String ContactNo { get; set; }
        public String HintQuestion { get; set; }
        public String HintAnswer { get; set; }
        public Boolean IsUserActive { get; set; }

        //Auditing Fields
        public DateTime CreatedOn { get; set; }
        public DateTime LastPassUpdate { get; set; }
        public DateTime LastLogin { get; set; }
    }
3

ユーザー@andygjpの回答に基づいて、ベースのDb.SaveChanges()メソッドをオーバーライドし、SqlDateTime.MinValueとSqlDateTime.MaxValueの間に収まらない日付をオーバーライドする関数を追加するとよいでしょう。

サンプルコードはこちら

public class MyDb : DbContext
{
    public override int SaveChanges()
    {
        UpdateDates();
        return base.SaveChanges();
    }

    private void UpdateDates()
    {
        foreach (var change in ChangeTracker.Entries().Where(x => (x.State == EntityState.Added || x.State == EntityState.Modified)))
        {
            var values = change.CurrentValues;
            foreach (var name in values.PropertyNames)
            {
                var value = values[name];
                if (value is DateTime)
                {
                    var date = (DateTime)value;
                    if (date < SqlDateTime.MinValue.Value)
                    {
                        values[name] = SqlDateTime.MinValue.Value;
                    }
                    else if (date > SqlDateTime.MaxValue.Value)
                    {
                        values[name] = SqlDateTime.MaxValue.Value;
                    }
                }
            }
        }
    }
}

https://stackoverflow.com/a/11297294/915812 に関するユーザー@ sky-devのコメントから取得

1
Hamza Khanzada

私は同じ問題を抱えていて、私の場合は日付をDateTime.Nowではなくnew DateTime()に設定していました

1
sam

1行でこれが修正されます。

modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));

だから、私のコードでは、私は追加しました:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
}

その1行をDBContextサブクラスオーバーライドvoid OnModelCreatingセクションに追加すると機能するはずです。

0
Howie Krauth

私の場合、これはエンティティを使用し、sqlテーブルにdatetime == getdate()のデフォルト値があるときに起こりました。このフィールドに値を設定するために私がしたこと。

0
Ahmad Moayad

私の場合、EF6でリファクタリングを行った後、テストは元のポスターと同じエラーメッセージで失敗しましたが、ソリューションはDateTimeフィールドとは関係ありませんでした。

エンティティを作成するときに、必須フィールドが欠落していました。不足しているフィールドを追加すると、エラーはなくなりました。私のエンティティには2つのDateTimeがありますか?フィールドですが、それらは問題ではありませんでした。

0
GrayDwarf

私はDatabase Firstを使用していますが、このエラーが発生したときの解決策は、edmxファイルでProviderManifestToken = "2005"を強制することでした(モデルとSQL Server 2005との互換性を確保する)。同様のことがCode Firstで可能かどうかわからない。

0
Sérgio Azevedo