2012-12-06 23 views
10

我真的有在具有的Guid作爲主鍵數據庫中插入新的實體打交道時面臨着以下問題 - EF 5碼第一做法。EF,代碼第一次 - 如何設置自定義的Guid標識值上插入

我知道有很多類似的話題,因爲我在這個問題上浪費了幾個小時,但是我無法找到這個問題的主題。

舉個例子,我的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<Entity> Entities { get; set; } 
} 

RoleCode和名稱只是文本數據,即在管理面板可編輯的,所以不`噸考慮這些字段名稱。

添加新實體時,我不指定主鍵值。到目前爲止,這一切都很好。作爲需要值且具有自動生成值的主鍵字段,它應該不總是指定Id,但是如果我設置了值,則應該保留它(如果啓用標識插入)。

但在某些情況下,我想指定一個主鍵值,例如用於我的數據庫的初始種子值的值。我需要它進行後續處理 - 讓我們說我需要那個特定的Guid在那裏。所以,如果我有我的配置類:

// 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("<some 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密鑰,它不適合我的工作,因爲它是非常罕見的情況。

從我現在唯一的解決辦法:

我最終是要覆蓋的SaveChanges()中的DbContext的方法和修改必須添加州的所有實體(我把這個想法從here) :

/// <summary> Custom processing when saving entities in changetracker </summary> 
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<DatabaseGeneratedAttribute>(); 
     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,因爲我thank EF5應該能夠處理這種情況。如果標識插入處於打開狀態,它適用於Int身份。

那麼有人有一個想法如何實現問題的更好的解決方案?

+0

我們使用EF 5 code-first,並在我們的實體的關鍵屬性上設置'[Key]'屬性。每次我們添加一個沒有設置密鑰的實體,然後調用'context.SaveChanges()',密鑰就會自動生成並且可以從實體對象中獲得。主要區別在於我們的密鑰是整數而不是Guids - 不確定是否重要。 –

+0

是的,與整數它工作正常,因爲我認爲它是數據庫中的需求,並且SQL Server爲它生成新的ID –

回答

2

您可以將Id標記爲虛擬屬性。因此,在您創建虛假數據的項目中,您可以使用帶有重寫ID的內部EntityRole,在這裏您可以刪除DatabaseGeneratedOption。Identity屬性或在DatabaseGeneratedOption.None上更改它。

+0

基本上我一直在尋找其他解決方案,但沒有發現比覆蓋SaveChanges的方法更好,因爲我在問題中顯示。它現在工作正常,沒有問題,如果我有價值的Id或不,所以我認爲現在就去。無論如何thx提示。 –

3

爲你做這個,因爲它是一個GUID最簡單的方法是有一個構造函數在類像

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

,並刪除數據庫生成的選項或將其更改爲DatabaseGeneratedOption.None。

+0

對於少量的模型類看起來很好,但在我的問題是模擬數據庫生成的選項,並沒有爲所有的數據庫模型做這個。我們有大約50人,並且正在增長 - 對於他們每個人,我都必須爲Id創建帶有1行的構造函數。另一個問題是,當我們不是數據庫上下文時,我們正在檢查Id,以確定是否從數據庫中獲取了此模型,或者它剛剛創建並仍未保存(例如,在助手類中)。 –