将Entity Framework Core中Attach用作Upsert及存储生成键相关问询
Great question! Let's break this down step by step, covering both the SQL Server Management Studio (SSMS) setup and how Entity Framework (EF) in C# recognizes this configuration.
1. Setting Up the Store-Generated uniqueidentifier in SSMS
If your table already exists, here's how to modify the Id column to be auto-generated by the database:
- Open SSMS, navigate to your target database, and expand the table you want to adjust.
- Right-click the table and select Design to open the Table Designer.
- Select your
Idcolumn (the uniqueidentifier primary key). - In the Column Properties pane (usually on the right side), scroll down to the Default Value or Binding field.
- Enter
NEWID()if you want a random GUID generated for each new row. - Or enter
NEWSEQUENTIALID()if you want sequentially ordered GUIDs (this is better for index performance, especially with large tables, as it reduces fragmentation).
- Enter
- Double-check that the column is set as the primary key (you'll see a key icon next to it; if not, right-click the column and select Set Primary Key).
- Save the table design (Ctrl+S or the save icon) to apply your changes.
Note:
NEWSEQUENTIALID()generates GUIDs ordered within the same machine, which is more efficient for indexing than the fully randomNEWID(). Opt for this if performance is a priority.
2. Configuring Entity Framework to Recognize the Store-Generated Key
How you inform EF about this database-generated column depends on whether you're using EF Core or EF6, and whether you're working with Code First or Database First.
For EF Core
Option 1: Data Annotations
Add the [DatabaseGenerated] attribute to your entity's Id property, specifying that it's generated on add:
using System.ComponentModel.DataAnnotations.Schema; public class YourEntity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } // Other entity properties... }
Option 2: Fluent API
If you prefer Fluent configuration (add this to your DbContext's OnModelCreating method):
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<YourEntity>() .Property(e => e.Id) .ValueGeneratedOnAdd(); // Tells EF the value is generated when the entity is inserted }
For EF6
Option 1: Data Annotations
Similar to EF Core, use the same [DatabaseGenerated] attribute:
using System.ComponentModel.DataAnnotations.Schema; public class YourEntity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } // Other entity properties... }
Option 2: Fluent API
Add this configuration to your DbContext's OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<YourEntity>() .Property(e => e.Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); }
Database First Scenario
If you're generating your EF model from an existing database (Database First), EF will automatically detect the DEFAULT constraint you set in SSMS. When you update your model from the database, the entity's Id property will have its StoreGeneratedPattern set to Identity—you can verify this in the EDMX designer or the generated code.
How This Affects DbSet.Attach
As you noted from oneunicorn's explanation:
DbSet.Attach会将对象图中的所有实体置于Unchanged状态;但若实体拥有存储生成键(如Identity列)且未设置键值,则会被置于Added状态。
With your Id configured as store-generated, when you attach an entity where Id is Guid.Empty (or not explicitly set), EF recognizes that the database will generate the value on insert. Instead of marking the entity as Unchanged, it will mark it as Added—meaning EF will send an INSERT command to the database, and the generated Id will be populated back into your entity after saving.
内容的提问来源于stack exchange,提问作者William Jockusch

