如何強制實體框架插入標識列?
我想編寫一些 C# 程式碼來用一些種子數據初始化我的數據庫。顯然,這將需要能夠在插入時設置各種標識列的值。我正在使用程式碼優先的方法。預設情況下,
DbContext處理數據庫連接,因此您不能SET IDENTITY_INSERT [dbo].[MyTable] ON. 所以,到目前為止我所做的是使用DbContext讓我指定要使用的數據庫連接的建構子。然後,我設置IDENTITY_INSERT為該ON數據庫連接,然後嘗試使用實體框架插入我的記錄。這是我到目前為止的一個例子:public class MyUserSeeder : IEntitySeeder { public void InitializeEntities(AssessmentSystemContext context, SqlConnection connection) { context.MyUsers.Add(new MyUser { MyUserId = 106, ConceptPersonId = 520476, Salutation = "Mrs", Firstname = "Novelette", Surname = "Aldred", Email = null, LoginId = "520476", Password="28c923d21b68fdf129b46de949b9f7e0d03f6ced8e9404066f4f3a75e115147489c9f68195c2128e320ca9018cd711df", IsEnabled = true, SpecialRequirements = null }); try { connection.Open(); SqlCommand cmd = new SqlCommand("SET IDENTITY_INSERT [dbo].[MyUser] ON", connection); int retVal = cmd.ExecuteNonQuery(); context.SaveChanges(); } finally { connection.Close(); } } }如此接近但到目前為止 - 因為,雖然
cmd.ExecuteNonQuery()工作正常,但當我執行時context.SaveChanges(),我被告知“當 IDENTITY_INSERT 設置為 ON 或複制使用者時,必須為表 ‘MyUser’ 中的標識列指定顯式值插入到 NOT FOR REPLICATION 標識列中。”據推測,因為 MyUserId(即 MyUser 表中的 Identity 列)是主鍵,所以實體框架在我呼叫時不會嘗試設置它
context.SaveChanges(),即使我為MyUser實體提供了MyUserId屬性值。那麼有沒有辦法強制實體框架嘗試插入實體的主鍵值呢?或者可能是一種臨時標記
MyUserId為不是主鍵值的方法,所以 EF 嘗試插入它?
經過仔細考慮,我認為實體框架拒絕插入標識列是一個特性,而不是一個錯誤。:-) 如果我要在我的數據庫中插入所有條目,包括它們的標識值,我還必須為實體框架為我自動創建的每個連結表創建一個實體!這不是正確的方法。
所以我正在做的是設置只使用 C# 程式碼並創建 EF 實體的種子類,然後使用 a
DbContext來保存新創建的數據。將轉儲的 SQL 轉換為 C# 程式碼需要更長的時間,但沒有(也不應該)過多的數據只是用於“播種”數據 - 它應該是少量具有代表性的數據可以在實時數據庫中快速放入新數據庫以進行調試/開發的那種數據。這確實意味著如果我想將實體連結在一起,我必須對已經插入的內容進行查詢,否則我的程式碼將不知道它們生成的標識值,例如。在我設置並完成後,這種事情將出現在種子程式碼context.SaveChanges中MyRoles:var roleBasic = context.MyRoles.Where(rl => rl.Name == "Basic").First(); var roleAdmin = context.MyRoles.Where(rl => rl.Name == "Admin").First(); var roleContentAuthor = context.MyRoles.Where(rl => rl.Name == "ContentAuthor").First(); MyUser thisUser = context.MyUsers.Add(new MyUser { Salutation = "Mrs", Firstname = "Novelette", Surname = "Aldred", Email = null, LoginUsername = "naldred", Password="c1c966821b68fdf129c46de949b9f7e0d03f6cad8ea404066f4f3a75e11514748ac9f68695c2128e520ca0275cd711df", IsEnabled = true, SpecialRequirements = null }); thisUser.Roles.Add(roleBasic);這樣做也使我更有可能在更改架構時更新我的種子數據,因為我可能會在更改它時破壞種子程式碼(如果我刪除欄位或實體,則使用該欄位的現有種子程式碼/entity 將無法編譯)。使用 SQL 腳本進行播種,情況並非如此,SQL 腳本也不會與數據庫無關。
所以我認為,如果您嘗試設置實體的身份欄位以進行 DB 播種數據,那麼您肯定採取了錯誤的方法。
如果我實際上將大量數據從 SQL Server 拖到 PostgreSQL(一個完整的實時數據庫,而不僅僅是一些種子數據),我可以通過 EF 來完成,但我希望同時打開兩個上下文,並編寫一些程式碼以從源上下文中獲取所有各種實體並將它們放入目標上下文中,然後保存更改。
通常,唯一適合插入標識值的時間是當您從一個 DB 複製到同一 DBMS 中的另一個 DB(SQL Server -> SQL Server、PostgreSQL -> PostgreSQL 等)時,然後您會這樣做它在 SQL 腳本中,而不是 EF 程式碼優先(SQL 腳本不會與 DB 無關,但它不需要;您不會在不同的 DBMS 之間切換)。
EF 6方法,使用msdn 文章:
using (var dataContext = new DataModelContainer()) using (var transaction = dataContext.Database.BeginTransaction()) { var user = new User() { ID = id, Name = "John" }; dataContext.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [dbo].[User] ON"); dataContext.User.Add(user); dataContext.SaveChanges(); dataContext.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [dbo].[User] OFF"); transaction.Commit(); }更新:為避免錯誤“當 IDENTITY_INSERT 設置為 ON 或複制使用者插入 NOT FOR REPLICATION 標識列時,必須為表 ‘TableName’ 中的標識列指定顯式值”,您應該更改StoreGeneratedPattern屬性的值模型設計器中從 Identity 到None的標識列。
請注意,將 StoreGeneratedPattern 更改為 None 將無法插入沒有指定 id 的對象(正常方式),並出現錯誤“當 IDENTITY_INSERT 設置為 OFF 時,無法在表 ‘TableName’ 中插入標識列的顯式值”。