web-dev-qa-db-ja.com

Entity Framework 6 GUIDをプライマリキーとして:列 'Id'、テーブル 'FileStore'に値NULLを挿入できません。列はヌルを許可しません

Guidであるプライマリキー「Id」を持つエンティティがあります。

public class FileStore
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Path { get; set; }
}

そしていくつかの構成:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<FileStore>().Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    base.OnModelCreating(modelBuilder);
}

レコードを挿入しようとすると、次のエラーが表示されます。

列 'Id'、テーブル 'FileStore'に値NULLを挿入できません。列はヌルを許可しません。 INSERTが失敗しました。\ r\nステートメントは終了しました。

Guidを手動で生成したくありません。レコードを挿入して、SQL Serverによって生成されるIdを取得するだけです。 .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)を設定すると、Id列はSQL ServerのID列ではありません。

SQL ServerでGUIDを自動生成するようにEntity Frameworkを構成するにはどうすればよいですか?

70
Algirdas

Id列にこれらの属性を追加することに加えて:

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

移行では、CreateTableを変更してdefaultValueSQLプロパティを列に追加する必要があります。

Id = c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"),

これにより、コメントで指摘したように、Code Firstで回避したいデータベースに手動で触れる必要がなくなります。

91
lightyeare

これを試して :

public class FileStore
 {
   [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public Guid Id { get; set; }
   public string Name { get; set; }
   public string Path { get; set; }
 }

これを確認できます SO post

16
user3383479

データベースのIDのデフォルト値をnewsequentialid()またはnewid()に設定できます。次に、EFのID設定が機能するはずです。

8
Murdock

それは前に私に起こりました。

テーブルが作成され、.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)に後で追加したときに、コードの移行でGuid列にデフォルト値を割り当てることができませんでした。

修正:

必要なのは、データベースに移動して、Id列を選択し、newsequentialid()を手動でDefault Value or Bindingに追加することです。

Dbo .__ MigrationHistoryテーブルを更新する必要はありません。

それが役に立てば幸い。


理論的には、isが誤って重複する可能性があるため、New Guid()を追加する解決策は一般的に好ましくありません。


また、データベースで直接編集することについて心配する必要はありません。 Entity Frameworkが行うことはすべて、データベース作業の一部を自動化することです。

翻訳中

.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)

[Id] [uniqueidentifier] NOT NULL DEFAULT newsequentialid(),

何らかの理由でEFが1つのことを逃し、デフォルト値を追加しなかった場合は、手動で追加してください。

5
Blaise

これは私(Azureなし)、開発サーバー上のSQL 2008 R2、またはローカルワークステーション上のlocaldb\mssqllocaldbで機能します。注:エンティティは、Create、CreateBy、Modified、ModifiedBy、およびVersion列を追加します。

public class Carrier : Entity
{
    public Guid Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}

次に、マッピング構成クラスを作成します

public class CarrierMap : EntityTypeConfiguration<Carrier>
{
    public CarrierMap()
    {
        HasKey(p => p.Id);

        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        Property(p => p.Code)
            .HasMaxLength(4)
            .IsRequired()
            .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute { IsClustered = true, IsUnique = true }));

        Property(p => p.Name).HasMaxLength(255).IsRequired();
        Property(p => p.Created).HasPrecision(7).IsRequired();
        Property(p => p.Modified)
            .HasColumnAnnotation("IX_Modified", new IndexAnnotation(new IndexAttribute()))
            .HasPrecision(7)
            .IsRequired();
        Property(p => p.CreatedBy).HasMaxLength(50).IsRequired();
        Property(p => p.ModifiedBy).HasMaxLength(50).IsRequired();
        Property(p => p.Version).IsRowVersion();
    }
}

このようにadd-migrationを実行すると、初期DbMigrationにUpメソッドが作成されます

        CreateTable(
            "scoFreightRate.Carrier",
            c => new
                {
                    Id = c.Guid(nullable: false, identity: true),
                    Code = c.String(nullable: false, maxLength: 4),
                    Name = c.String(nullable: false, maxLength: 255),
                    Created = c.DateTimeOffset(nullable: false, precision: 7),
                    CreatedBy = c.String(nullable: false, maxLength: 50),
                    Modified = c.DateTimeOffset(nullable: false, precision: 7,
                        annotations: new Dictionary<string, AnnotationValues>
                        {
                            { 
                                "IX_Modified",
                                new AnnotationValues(oldValue: null, newValue: "IndexAnnotation: { }")
                            },
                        }),
                    ModifiedBy = c.String(nullable: false, maxLength: 50),
                    Version = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
                })
            .PrimaryKey(t => t.Id)
            .Index(t => t.Code, unique: true, clustered: true);

注:Id列はデフォルト値を取得しないので、心配しないでください

次に、Update-Databaseを実行すると、データベース内に次のようなテーブル定義が作成されます。

CREATE TABLE [scoFreightRate].[Carrier] (
    [Id]         UNIQUEIDENTIFIER   DEFAULT (newsequentialid()) NOT NULL,
    [Code]       NVARCHAR (4)       NOT NULL,
    [Name]       NVARCHAR (255)     NOT NULL,
    [Created]    DATETIMEOFFSET (7) NOT NULL,
    [CreatedBy]  NVARCHAR (50)      NOT NULL,
    [Modified]   DATETIMEOFFSET (7) NOT NULL,
    [ModifiedBy] NVARCHAR (50)      NOT NULL,
    [Version]    ROWVERSION         NOT NULL,
    CONSTRAINT [PK_scoFreightRate.Carrier] PRIMARY KEY NONCLUSTERED ([Id] ASC)
);


GO
CREATE UNIQUE CLUSTERED INDEX [IX_Code]
    ON [scoFreightRate].[Carrier]([Code] ASC);

注:SqlServerMigrationSqlGeneratorをオーバーライドして、プライマリキーがクラスター化インデックスにならないようにします。開発者がテーブルにクラスター化インデックスをより適切に設定することをお勧めします

public class OurMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(AddPrimaryKeyOperation addPrimaryKeyOperation)
    {
        if (addPrimaryKeyOperation == null) throw new ArgumentNullException("addPrimaryKeyOperation");
        if (!addPrimaryKeyOperation.Table.Contains("__MigrationHistory"))
            addPrimaryKeyOperation.IsClustered = false;
        base.Generate(addPrimaryKeyOperation);
    }

    protected override void Generate(CreateTableOperation createTableOperation)
    {
        if (createTableOperation == null) throw new ArgumentNullException("createTableOperation");
        if (!createTableOperation.Name.Contains("__MigrationHistory"))
            createTableOperation.PrimaryKey.IsClustered = false;
        base.Generate(createTableOperation);
    }

    protected override void Generate(MoveTableOperation moveTableOperation)
    {
        if (moveTableOperation == null) throw new ArgumentNullException("moveTableOperation");
        if (!moveTableOperation.CreateTableOperation.Name.Contains("__MigrationHistory")) moveTableOperation.CreateTableOperation.PrimaryKey.IsClustered = false;
        base.Generate(moveTableOperation);
    }
}
5
Allan

Entity Framework – Guidを主キーとして使用する

Entity Frameworkを使用する場合、テーブルの主キーとしてGuidを使用するには、整数を使用する場合よりも少し手間がかかります。設定方法は、その方法を読んだり説明した後は簡単です。

Code FirstとDatabase Firstのアプローチでは、プロセスが少し異なります。この投稿では、両方の手法について説明します。

ここに画像の説明を入力

最初のコード

コードファーストアプローチを採用する場合、主キーとしてGUIDを使用するのは簡単です。エンティティを作成するときに、以下に示すように、主キープロパティにDatabaseGenerated属性を追加します。

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }

エンティティフレームワークは、主キーとuniqueidentifierデータ型を使用して、期待どおりに列を作成します。

codefirst-defaultvalue

また、非常に重要なことに、列のデフォルト値が(newsequentialid())に設定されていることに注意してください。これにより、行ごとに新しい連続(連続)GUIDが生成されます。そうしたい場合は、これをnewid())に変更すると、新しい行ごとに完全にランダムなGUIDになります。これは、データベースが削除されて再作成されるたびにクリアされるため、Database Firstアプローチをとる場合に効果的です。

最初のデータベース

データベースファーストのアプローチは、コードファーストのアプローチと同様の行に従いますが、モデルを手動で編集して機能させる必要があります。

何かを行う前に、主キー列を編集し、(newsequentialid())または(newid())関数をデフォルト値として追加してください。

ここに画像の説明を入力

次に、EDMXダイアグラムを開き、適切なプロパティを選択して、プロパティウィンドウを開きます。 StoreGeneratedPatternがidentityに設定されていることを確認してください。

databasefirst-model

コードでエンティティにIDを与える必要はありません。IDは、エンティティがデータベースにコミットされた後に自動的に入力されます。

using (ApplicationDbContext context = new ApplicationDbContext())
{
    var person = new Person
                     {
                         FirstName = "Random",
                         LastName = "Person";
                     };

    context.People.Add(person);
    context.SaveChanges();
    Console.WriteLine(person.Id);
}

重要な注意:Guidフィールドは主キーでなければなりません。そうでないと機能しません。 Entity Frameworkは、かなり不可解なエラーメッセージを表示します!

概要

Guid(Globally Unique Identifiers)は、Entity Frameworkの主キーとして簡単に使用できます。実行するアプローチに応じて、これを行うには少し余分な労力が必要です。コードファーストアプローチを使用する場合、DatabaseGenerated属性をキーフィールドに追加します。 Database Firstアプローチを採用する場合、モデルでStoredGeneratedPatternをIdentityに明示的に設定します。

[1]: https://i.stack.imgur.com/IxGdd.png
[2]: https://i.stack.imgur.com/Qssea.png
4
Vinu Das

this によれば、DatabaseGeneratedOption.Identityが追加された場合、特定の移行によって検出されませんafterテーブルが作成されました。そのため、データベースとその特定の移行を削除し、新しい移行を追加し、最後にデータベースを更新すると、すべてが期待どおりに機能します。 EF 6.1、SQL2014、およびVS2013を使用しています。

4
stt106

Code-Firstを実行し、すでにデータベースがある場合:

public override void Up()
{
    AlterColumn("dbo.MyTable","Id", c =>  c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"));
}
2
Someone OnEarth

できません。あなたは多くのものを壊すでしょう。関係が好きです。 EFは、設定された方法では実行できない、引き戻される数値に依存します。すべてのパターンを壊すための価格があります。

C#レイヤーでGUIDを生成して、関係が引き続き機能できるようにします。

1
TomTom

そして、このようなものは何ですか?

public class Carrier : Entity
{
    public Carrier()
    {
         this.Id = Guid.NewGuid();
    }  
    public Guid Id { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}
0
Miroslav Siska