EF,Code First – 如何在插入时设置自定义Guid标识值

在处理在Guid作为主键的数据库中插入新实体时,我面临以下问题 – 使用Code第一种方法的EF 5

我知道有很多类似的主题,因为我在这个问题上已经流了好几个小时,但是我找不到这个问题的主题。

举个例子,我的POCO课程是:

public class EntityRole : IAdminModel { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } [Required] [MaxLength(50)] public string Name { get; set; } [Required] [Display(Name = "Role code")] [MaxLength(20)] public string RoleCode { get; set; } [Display(Name = "Entities Assigned")] [InverseProperty("Role")] public List Entities { get; set; } } 

RoleCode和Name只是文本数据,可以在管理面板中编辑,因此不要考虑这些字段名称。

添加新实体时,我不指定主键值。 到目前为止一切都那么好,这里一切正常。 作为需要值并且具有自动生成值的主键字段,它应该不总是指定Id,但是如果我设置了值,则应该保留它(如果启用了标识插入)。

但在某些情况下,我想指定一个主键值,例如我的数据库的初始种子值。 我需要它以便以后处理 – 我只想说我需要那个特定的Guid。 所以如果我在我的Configuration类中:

 // Initial data seed protected override void Seed(WebPortalDbContext context) { context.MenuItems.AddOrUpdate( m => m.Id, new EntityRole {Id = new Guid("268bf332-910e-4ea1-92f8-1ac0611f4c62"), Name = "Some name", RoleCode = "SN"}, ); } 

即使我定期添加,Guid键设置也不起作用:

 using (var context = new MyDBContext()) { context.MenuItems.Add( new Entity() {Id = new Guid(""), Name = "fooname" /*some other values*/} ); context.SaveChanges(); } 

我在SQL Server跟踪中的内容是:

 exec sp_executesql N'declare @generated_keys table([Id] uniqueidentifier) insert [dbo].[EntityRoles]([Name], [RoleCode]) output inserted.[Id] into @generated_keys values (@0, @1) select t.[Id] from @generated_keys as g join [dbo].[EntityRoles] as t on g.[Id] = t.[Id] where @@ROWCOUNT > 0',N'@0 nvarchar(50),@1 nvarchar(20)',@0=N'Chief Captain',@1=N'CO1' 

这里很明显,新的Guid值不是从EF SQL生成器发送到SQL Server,所以问题出在EF中。

所以我删除了DatabaseGeneratedOption.Identity属性,然后就可以了,但是我丢失了Id键的自动生成,这对我来说不起作用,因为这是非常罕见的情况。

我现在唯一的解决方案:

我最后是覆盖DBContext的SaveChanges()方法并修改所有要添加状态的实体(我从这里接受了想法):

 ///  Custom processing when saving entities in changetracker  public override int SaveChanges() { // recommended to explicitly set New Guid for appropriate entities -- http://msdn.microsoft.com/en-us/library/dd283139.aspx foreach (var entry in ChangeTracker.Entries().Where(e => e.State == EntityState.Added)) { var t = entry.Entity.GetType(); if (t.GetProperty("Id") == null) continue; var info = t.GetProperty("Id").GetCustomAttributes(typeof (DatabaseGeneratedAttribute), true).Cast(); if (!info.Any() || info.Single().DatabaseGeneratedOption != DatabaseGeneratedOption.Identity) { if (t.GetProperty("Id").PropertyType == typeof(Guid) && (Guid)t.GetProperty("Id").GetValue(entry.Entity, null) == Guid.Empty) t.GetProperty("Id").SetValue(entry.Entity, Guid.NewGuid(), null); } } return base.SaveChanges(); } 

与此相结合,应删除所有DatabaseGeneratedOption 。 所有模型都有名为“Id”的主键,遵循命名约定的最佳实践主题之一。

但这看起来并不是非常优雅的workaronud,因为我认为EF5应该能够处理这种情况。 如果启用了标识插入,它适用于Int标识。

那么有人知道如何在问题上实现更好的解决方案吗?

你这样做的简单方法是GUID,就是在你的类中有一个构造函数

 public EntityRole() { Id = Guid.NewGuid(); } 

并删除数据库生成选项或将其更改为DatabaseGeneratedOption.None。

您可以将Id标记为虚拟财产。 因此,在您创建虚假数据的项目中,您可以使用带有覆盖ID的内部EntityRole,在这里您可以删除DatabaseGeneratedOption.Identity属性或在DatabaseGeneratedOption.None上更改它。