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を構成するにはどうすればよいですか?
Id列にこれらの属性を追加することに加えて:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
移行では、CreateTable
を変更してdefaultValueSQL
プロパティを列に追加する必要があります。
Id = c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"),
これにより、コメントで指摘したように、Code Firstで回避したいデータベースに手動で触れる必要がなくなります。
これを試して :
public class FileStore
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Name { get; set; }
public string Path { get; set; }
}
これを確認できます SO post 。
データベースのIDのデフォルト値をnewsequentialid()またはnewid()に設定できます。次に、EFのID設定が機能するはずです。
それは前に私に起こりました。
テーブルが作成され、.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つのことを逃し、デフォルト値を追加しなかった場合は、手動で追加してください。
これは私(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);
}
}
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
this によれば、DatabaseGeneratedOption.Identityが追加された場合、特定の移行によって検出されませんafterテーブルが作成されました。そのため、データベースとその特定の移行を削除し、新しい移行を追加し、最後にデータベースを更新すると、すべてが期待どおりに機能します。 EF 6.1、SQL2014、およびVS2013を使用しています。
Code-Firstを実行し、すでにデータベースがある場合:
public override void Up()
{
AlterColumn("dbo.MyTable","Id", c => c.Guid(nullable: false, identity: true, defaultValueSql: "newsequentialid()"));
}
できません。あなたは多くのものを壊すでしょう。関係が好きです。 EFは、設定された方法では実行できない、引き戻される数値に依存します。すべてのパターンを壊すための価格があります。
C#レイヤーでGUIDを生成して、関係が引き続き機能できるようにします。
そして、このようなものは何ですか?
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; }
}