2013-07-10 58 views
3

我們正在開發一個使用C#4和SQL Server 2008 R2的系統,並遵循域驅動的設計原則。沒有使用ORM。在有些情況下,我們必須持有另一實體的集合的實體的情況下,如:更新數據庫集合的最佳策略

public class Project 
{ 
    public IdCollection ParticipantUsers 
    { 
     get 
     { 
      //... 
     } 
    } 

    public void AddParticipantUser(Id idUser) 
    { 
     //... 
    } 

    public void RemoveParticipantUser(Id idUser) 
    { 
     //... 
    } 
} 

ProjectParticipantUsers屬性返回參與其中的用戶ID的集合。 AddParticipantUserRemoveParticipantUser方法,好吧,你明白了。

我們發現的問題如下。將項目實例發送到存儲庫以在數據庫上進行更新時,ParticipantUsers ids集合可能已從上次檢索時發生更改:某些id可能已被添加,有些可能已被刪除,有些可能是一模一樣。我們可以使用強力方法,並從數據庫中刪除該項目的所有參與用戶,然後重新插入當前的用戶,但我想探索更好的方法。

您能想到哪些更好的方法,允許我只插入已添加的那些id,刪除那些已被刪除的id,並保留那些仍然存在於collection中的那些?

+0

因爲它沒有被標記爲這樣,我猜你的倉庫沒有使用任何種類的ORM(EF/NHibernate)? –

+2

從數據庫中獲取當前列表。與'ParticipantUsers'比較。如果缺少則添加,如果不再在「ParticipantUsers」中移除,則不要觸摸 – musefan

+0

@SimonBelanger:正確,無ORM。我會爲我的OP添加一個說明。 – CesarGon

回答

2

其實我認爲刪除重新插入方法比任何基於比較的解決方案更好。

首先,它很容易實現。其次,它避免了用於比較的另一個數據提取。

你能否進一步解釋爲什麼這種方法不是首選,也許我不理解它。

+0

這是ORM通常使用的方法,因爲它們可以確定每個值對象的唯一鍵。 – eulerfx

+0

更正:*可能不是* – eulerfx

+0

事實上,我們的值對象沒有唯一的鍵(好,除了完整的值集)。 – CesarGon

0

orgData - 是包含

newData(在這種情況下更改之前IdCollection ParticipantUsers)從數據庫中的原始數據列表/表/其他枚舉對象 - 在列表/表/其他枚舉對象包含您的數據更新

var orgList = orgData.Select(a => a.Id); 
var newList = newData.Select(a => a.Id); 

var itemsToAdd = newData.Where(a => !orgList.Contains(a.Id)); 
var itemsToDel = orgData.Where(a => !newList.Contains(a.Id)); 
+0

你可以添加一些上下文信息嗎?什麼是orgData和newData?謝謝。 – CesarGon

+0

那麼你是否建議我在每次更新之前從數據庫中檢索集合,正如musefan在OP的評論中所建議的那樣? – CesarGon

+0

是或者你在行更新行並在更新期間處理System.Data.DBConcurrencyException - 它返回一個導致異常的行。無論如何,在某些時候,您必須將您當前的數據與來自數據庫的數據進行比較,正如您所說,可以在此期間進行修改。 – Arie

2

我猜事件採購是,如果你做太多這樣的事情的理想,但通常我們作爲開發商,沒有得到利用新技術多:)

因爲我不是一個ORM的風扇我以前所做的只是簡單地列出與工作單元所做的相同的改變。因此,每次添加參與者時,我都會將其添加到AddedParticipants集合中,並且爲了移除參與者,我將添加到RemovedParticipants集合中。我想我們可以進一步將它帶入每個條目的變化類型列表中。但我相信你明白了。

存儲庫然後將使用跟蹤的更改來執行相關的數據操作。可能不是最漂亮的解決方案,但它可以完成工作。

我想補充一點的短名單我也簡單地刪除和重新插入,但正如你所說,它可能是在這種情況下有些沉重和一個尺寸不適合所有:)

當然重裝如Arie所建議的,名單和比較工作會很好。

+0

謝謝。這也可以工作。 – CesarGon

1

我們的方法(使用SQL Server 2008 R2)是將表值參數(TVP)與合併語句結合使用。

這種方式代碼將項目列表作爲表格傳遞給存儲過程。 proc然後在插入/更新/刪除的合併語句中使用該表。

你需要一個用戶定義的表類型,例如:

CREATE TYPE [dbo].[Participants] AS TABLE(
    [ProjectId] [int] NOT NULL, 
    [ParticipantId] [int] NOT NULL, 
    PRIMARY KEY CLUSTERED 
(
    [ProjectId] ASC, 
    [ParticipantId] ASC 
)WITH (IGNORE_DUP_KEY = OFF) 
) 
GO 

然後你需要一個C#類來保存數據。只需修改IdCollection是相似的:

public class IdCollection : List<ParticipantUsers>, IEnumerable<SqlDataRecord> { 
    #region IEnumerable<SqlDataRecord> Members 

    IEnumerator<SqlDataRecord> IEnumerable<SqlDataRecord>.GetEnumerator() { 
     SqlDataRecord rec = new SqlDataRecord(
      new SqlMetaData("ProjectId", System.Data.SqlDbType.Int), 
      new SqlMetaData("ParticipantId", System.Data.SqlDbType.Int) 
     ); 

     foreach (ParticipantUser ans in this) { 
      rec.SetInt32(0, ans.ProjectId); 
      rec.SetInt32(1, ans.ParticipantId); 
      yield return rec; 
     } 
    } 

    #endregion 

} 

你的存儲過程將是這個樣子:

CREATE PROCEDURE [dbo].[ParticpantUpdate] 
    @Particpants core.Participants READONLY 
AS 
BEGIN 
    SET NOCOUNT ON; 

    MERGE INTO dbo.ProjectParticipants pp 
     USING @Particpants RG 
      ON (RG.ProjectId = pp.ProjectId) 
      AND (RG.ParticipantId = pp.ParticipantId) 
     WHEN NOT MATCHED BY TARGET THEN 
      INSERT(ProjectId, ParticipantId) 
      VALUES(RG.ProjectId, RG.ParticipantId) 
     WHEN NOT MATCHED BY SOURCE 
       AND target.ProjectId in (SELECT ProjectId from @Participants) THEN 
       DELETE; 
    ; 


END 

要調用PROC(使用企業庫):

SqlDatabase db = DatabaseFactory.CreateDatabase("myconnstr") as SqlDatabase; 

using (DbCommand dbCommand = db.GetStoredProcCommand("dbo.ParticipantUpdate")) { 
    db.AddInParameter(dbCommand, "Participants", SqlDbType.Structured, participantList); 

    db.ExecuteNonQuery(dbCommand); 

} // using dbCommand 
+0

有趣的方法。我會考慮的。謝謝。 – CesarGon

+0

@CesarGon:已更新爲包含'MERGE'語句的正確'delete'部分 – NotMe

1

有無你考慮將所有的ID序列化到一個列中?這樣,你只是更新一個記錄。如果您擔心集合在此期間發生變化,您可能需要實現樂觀併發。

+0

我們在系統中總體使用了樂觀併發。但是我們仍然需要確定哪些實體已被添加和刪除。關於您對所有ID使用單個列的建議,這很有趣,謝謝。我會與同事討論。 – CesarGon