2013-07-10 57 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