diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/RelationshipService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/RelationshipService.java
index 50644afd58f751dc1ae2ea3c0b0d6987bd08ec82..bf61d70e73ce1dfdac3500f56a75aaf97cc780a7 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/RelationshipService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/RelationshipService.java
@@ -23,6 +23,7 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.util.SampleUtils;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.RelationshipUtils;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.DAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant;
+import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetRelationshipPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
@@ -144,21 +145,17 @@ public class RelationshipService implements IRelationshipService
     public void assignDataSetToExperiment(IAuthSession session, DataPE data, ExperimentPE experimentOrNull)
     {
         RelationshipUtils.setExperimentForDataSet(data, experimentOrNull, session);
-        if (experimentOrNull != null)
-        {
-            RelationshipUtils.setSampleForDataSet(data, null, session);
-        }
     }
 
     @Override
-    public void assignDataSetToSample(IAuthSession session, DataPE data, SamplePE sampleOrNull)
+    public void assignDataSetToSample(IAuthSession session, DataPE data, SamplePE sample)
     {
-        if (sampleOrNull != null)
+        SamplePE currentSample = data.tryGetSample();
+        if (equalEntities(currentSample, sample))
         {
-            ExperimentPE experiment = sampleOrNull.getExperiment();
-            RelationshipUtils.setExperimentForDataSet(data, experiment, session);
+            return;
         }
-        RelationshipUtils.setSampleForDataSet(data, sampleOrNull, session);
+        RelationshipUtils.setSampleForDataSet(data, sample, session);
     }
 
     @Override
@@ -281,4 +278,16 @@ public class RelationshipService implements IRelationshipService
         return RelationshipUtils.getParentChildRelationshipType(daoFactory.getRelationshipTypeDAO());
     }
 
+    private static <T extends IIdHolder >boolean equalEntities(T entity1OrNull, T entity2OrNull)
+    {
+        Long id1 = getIdOrNull(entity1OrNull);
+        Long id2 = getIdOrNull(entity2OrNull);
+        return id1 == null ? id1 == id2 : id1.equals(id2);
+    }
+
+    private static Long getIdOrNull(IIdHolder idHolderOrNull)
+    {
+        return idHolderOrNull == null ? null : idHolderOrNull.getId();
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractDataSetBusinessObject.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractDataSetBusinessObject.java
index c3c0c44ee1fbab34cfb651dfac762693a61aaecc..d9e66090a2a4088c8aa7024b3affb07108d8f5d8 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractDataSetBusinessObject.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/AbstractDataSetBusinessObject.java
@@ -34,6 +34,7 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.util.DataSetTypeWitho
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDataDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IEntityPropertiesConverter;
+import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetRelationshipPE;
@@ -118,30 +119,99 @@ public abstract class AbstractDataSetBusinessObject extends AbstractSampleIdenti
         {
             throw createWrongSampleException(data, newSample, "the new sample is shared");
         }
-        ExperimentPE experiment = newSample.getExperiment();
-        String dataSetTypeCode = data.getDataSetType().getCode();
+        assignDataSetToSampleAndExperiment(data, newSample, newSample.getExperiment());
+    }
+
+    private void assignDataSetToSampleAndExperiment(DataPE data, SamplePE newSample, ExperimentPE experiment)
+    {
+        List<DataSetSampleExperiment> assignments = new ArrayList<DataSetSampleExperiment>();
+        gatherNewAssigments(assignments, data, data, newSample, experiment);
+        for (DataSetSampleExperiment assignment : assignments)
+        {
+            assignment.assignDataSet(relationshipService, session);
+        }
+    }
+    
+    private void gatherNewAssigments(List<DataSetSampleExperiment> assignments, 
+            DataPE dataSet, DataPE rootDataSet, SamplePE sample, ExperimentPE experiment)
+    {
+        String dataSetTypeCode = dataSet.getDataSetType().getCode();
         if (experiment == null && dataSetTypeChecker.isDataSetTypeWithoutExperiment(dataSetTypeCode) == false)
         {
-            throw createWrongSampleException(data, newSample,
-                    "the new sample is not connected to any experiment");
+            throw createWrongSampleException(dataSet, sample,
+                    "the new sample is not connected to any experiment and the data set type ("
+                            + dataSetTypeCode + ") doesn't match one of the following regular expressions: "
+                            + dataSetTypeChecker.getRegularExpressions());
+        }
+        if (rootDataSet == dataSet)
+        {
+            assignments.add(new DataSetSampleExperiment(dataSet, sample, experiment));
+        }
+        SamplePE rootSample = rootDataSet.tryGetSample();
+        ExperimentPE rootExperiment = getExperimentOf(rootDataSet);
+        List<DataPE> components = dataSet.getContainedDataSets();
+        for (DataPE component : components)
+        {
+            SamplePE componentSample = component.tryGetSample();
+            ExperimentPE componentExperiment = getExperimentOf(component);
+            if ((equalEntities(rootSample, componentSample) || componentSample == null)
+                    && equalEntities(rootExperiment, componentExperiment))
+            {
+                SamplePE newSample = componentSample == null && rootSample != null ? null : sample;
+                assignments.add(new DataSetSampleExperiment(component, newSample, experiment));
+                gatherNewAssigments(assignments, component, rootDataSet, sample, experiment);
+            }
         }
-
-        relationshipService.assignDataSetToSample(session, data, newSample);
+    }
+    
+    private ExperimentPE getExperimentOf(DataPE data)
+    {
+        SamplePE sample = data.tryGetSample();
+        return sample == null ? data.getExperiment() : sample.getExperiment();
+    }
+    
+    private static <T extends IIdHolder >boolean equalEntities(T entity1OrNull, T entity2OrNull)
+    {
+        Long id1 = getIdOrNull(entity1OrNull);
+        Long id2 = getIdOrNull(entity2OrNull);
+        return id1 == null ? id1 == id2 : id1.equals(id2);
     }
 
-    protected void updateExperiment(DataPE data, ExperimentIdentifier experimentIdentifier)
+    private static Long getIdOrNull(IIdHolder idHolderOrNull)
     {
-        assert experimentIdentifier != null;
-        ExperimentPE experiment = getExperimentByIdentifier(experimentIdentifier);
-        updateExperiment(data, experiment);
+        return idHolderOrNull == null ? null : idHolderOrNull.getId();
     }
 
-    protected void updateExperiment(DataPE data, ExperimentPE experiment)
+    private static final class DataSetSampleExperiment
     {
-        if (experiment.equals(data.getExperiment()) == false)
+        private DataPE dataSet;
+        private SamplePE sample;
+        private ExperimentPE experiment;
+
+        DataSetSampleExperiment(DataPE dataSet, SamplePE sample, ExperimentPE experiment)
         {
-            relationshipService.assignDataSetToExperiment(session, data, experiment);
+            this.dataSet = dataSet;
+            this.sample = sample;
+            this.experiment = experiment;
         }
+
+        public void assignDataSet(IRelationshipService relationshipService, Session session)
+        {
+            if (equalEntities(dataSet.tryGetSample(), sample) == false)
+            {
+                relationshipService.assignDataSetToSample(session, dataSet, sample);
+            }
+            if (equalEntities(dataSet.getExperiment(), experiment) == false)
+            {
+                relationshipService.assignDataSetToExperiment(session, dataSet, experiment);
+            }
+        }
+    }
+
+    protected void updateExperiment(DataPE data, ExperimentIdentifier experimentIdentifier)
+    {
+        assert experimentIdentifier != null;
+        assignDataSetToSampleAndExperiment(data, null, getExperimentByIdentifier(experimentIdentifier));
     }
 
     protected ExperimentPE getExperimentByIdentifier(final ExperimentIdentifier identifier)
@@ -155,9 +225,7 @@ public abstract class AbstractDataSetBusinessObject extends AbstractSampleIdenti
             throw new UserFailureException("Unkown experiment because of unkown project: "
                     + identifier);
         }
-        final ExperimentPE exp =
-                getExperimentDAO().tryFindByCodeAndProject(project, identifier.getExperimentCode());
-        return exp;
+        return getExperimentDAO().tryFindByCodeAndProject(project, identifier.getExperimentCode());
     }
 
     protected void setContainedDataSets(final DataPE container,
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/DataBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/DataBO.java
index a4fd29fa9df561024c87aeddaf9bc30720345d73..fe6956e8ba1d67ba758954763a1dcf5be9419fff 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/DataBO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/DataBO.java
@@ -616,10 +616,10 @@ public class DataBO extends AbstractDataSetBusinessObject implements IDataBO
         {
             // update sample and indirectly experiment
             updateSample(data, sampleIdentifierOrNull);
-        } else
+        } else if (updates.getExperimentIdentifierOrNull() != null)
         {
-            relationshipService.assignDataSetToSample(session, data, null);
             updateExperiment(data, updates.getExperimentIdentifierOrNull());
+            relationshipService.assignDataSetToSample(session, data, null);
         }
 
         setParents(data, asListOrNull(updates.getModifiedParentDatasetCodesOrNull()));
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/util/DataSetTypeWithoutExperimentChecker.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/util/DataSetTypeWithoutExperimentChecker.java
index 0841024ff0d99858e04a6904338b8765ba5fe6cf..fc9d912efb045eb3049639e8a549084465595337 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/util/DataSetTypeWithoutExperimentChecker.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/util/DataSetTypeWithoutExperimentChecker.java
@@ -35,11 +35,13 @@ public class DataSetTypeWithoutExperimentChecker
 {
     public static final String PROPERTY_KEY = "data-set-types-with-no-experiment-needed";
     
-    private List<Pattern> patternsForDataSetTypesWithoutExperiment = new ArrayList<Pattern>();
+    private final String regularExpressions;
     
+    private List<Pattern> patternsForDataSetTypesWithoutExperiment = new ArrayList<Pattern>();
+
     public DataSetTypeWithoutExperimentChecker(Properties properties)
     {
-        String regularExpressions = properties.getProperty(PROPERTY_KEY);
+        regularExpressions = properties.getProperty(PROPERTY_KEY);
         if (StringUtils.isNotBlank(regularExpressions))
         {
             String[] splittedRegexes = regularExpressions.split(",");
@@ -57,6 +59,11 @@ public class DataSetTypeWithoutExperimentChecker
         }
     }
     
+    public String getRegularExpressions()
+    {
+        return regularExpressions;
+    }
+
     public boolean isDataSetTypeWithoutExperiment(String dataSetTypeCode)
     {
         for (Pattern pattern : patternsForDataSetTypesWithoutExperiment)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
index ebff4c49f5e8cd1e8bd9c9afceeb34ec5c97aff4..b0f030f8903b9e7ab57382698eb9a0996f85c5e4 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
@@ -637,13 +637,6 @@ public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements
         if (experiment != null)
         {
             experiment.addDataSet(this);
-            for (DataPE contained : getContainedDataSets())
-            {
-                if (false == contained.equals(this))
-                {
-                    contained.setExperiment(experiment);
-                }
-            }
         } else
         {
             ExperimentPE previous = getExperiment();
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/DataSetNode.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/DataSetNode.java
index 7e6e22ea9dc1e7ec4e4e3474db666dd632c39456..a4e25ff5e772f160d2c036485a8b112cf3626d35 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/DataSetNode.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/DataSetNode.java
@@ -103,7 +103,7 @@ public final class DataSetNode extends EntityNode
     @Override
     public String toString()
     {
-        StringBuilder builder = new StringBuilder(getCode());
+        StringBuilder builder = new StringBuilder(super.toString());
         if (deletable == false)
         {
             builder.append(" (non deletable)");
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/EntityNode.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/EntityNode.java
index a049dd9cfe4616048458d5a183801035c729837a..dfa18463e03ef45c61612f98a6bc3112d85424d3 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/EntityNode.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/EntityNode.java
@@ -18,7 +18,7 @@ package ch.systemsx.cisd.openbis.generic.server.business.bo.entitygraph;
 
 import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
 
-public class EntityNode implements IIdHolder
+public class EntityNode implements IIdHolder, Comparable<EntityNode>
 {
     private final long id;
 
@@ -47,9 +47,32 @@ public class EntityNode implements IIdHolder
     {
         return type;
     }
+    
+    public String getCodeAndType()
+    {
+        StringBuilder builder = new StringBuilder(code);
+        if (type != null)
+        {
+            builder.append("[").append(type).append("]");
+        }
+        return builder.toString();
+    }
 
     public void setType(String type)
     {
         this.type = type;
     }
+
+    @Override
+    public int compareTo(EntityNode that)
+    {
+        return this.getCode().compareTo(that.getCode());
+    }
+    
+    @Override
+    public String toString()
+    {
+        return getCodeAndType();
+    }
+    
 }
\ No newline at end of file
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/ExperimentNode.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/ExperimentNode.java
index d904a9513b61d5b791e1bacf2e2193ac8d06de42..5072c31effc24e9deeb3d6e521c19c761f3bc7a3 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/ExperimentNode.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/ExperimentNode.java
@@ -60,7 +60,7 @@ public final class ExperimentNode extends EntityNode
     @Override
     public String toString()
     {
-        StringBuilder builder = new StringBuilder(getCode());
+        StringBuilder builder = new StringBuilder(super.toString());
         Utils.appendTo(builder, "samples", samples);
         Utils.appendTo(builder, "data sets", dataSets);
         return builder.toString();
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/SampleNode.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/SampleNode.java
index 6a106977b85d922ad3dfe847811f557084391beb..b8fee0d01ee12211e453f62dfea73f89c8a15559 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/SampleNode.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/entitygraph/SampleNode.java
@@ -98,7 +98,7 @@ public final class SampleNode extends EntityNode
     public String toString()
     {
 
-        StringBuilder builder = new StringBuilder(getCode());
+        StringBuilder builder = new StringBuilder(super.toString());
         Utils.appendTo(builder, "experiment", experiment);
         Utils.appendTo(builder, "children", children);
         Utils.appendTo(builder, "parents", parents);
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/AssignDataSetToSampleTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/AssignDataSetToSampleTest.java
index f0b54bb7a9f7ebd7e5f23ecf4b9bc96c42b393d0..cee14dbe3d7c4acf388ab3298e46d6499b50e43c 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/AssignDataSetToSampleTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/AssignDataSetToSampleTest.java
@@ -63,134 +63,234 @@ public class AssignDataSetToSampleTest extends BaseTest
     Space destinationSpace;
     
     @Test
-    public void dataSetReassignedFromExperimentToExperiment()
+    public void containerWithSomeComponentsReassignedFromExperimentToExperiment()
     {
-        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1\n"
-                + "E2, samples: S2\n");
+        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1 DS2 DS4\n"
+                + "E2, samples: S2\n"
+                + "E3, data sets: DS3\n"
+                + "DS1, components: DS2 DS3\n"
+                + "DS2, components: DS4");
 
         reassignToExperiment(g.ds(1), g.e(2));
 
         assertEquals("E1, samples: S1\n"
-                + "E2, samples: S2, data sets: DS1\n", renderGraph(g));
+                + "E2, samples: S2, data sets: DS1 DS2 DS4\n"
+                + "E3, data sets: DS3\n"
+                + "DS1, components: DS2 DS3\n"
+                + "DS2, components: DS4\n", renderGraph(g));
         repository.assertModified(g.e(1), g.e(2));
-        repository.assertModified(g.ds(1));
-        repository.assertUnmodified(g.s(1), g.s(2));
+        repository.assertModified(g.ds(1), g.ds(2), g.ds(4));
+        repository.assertUnmodified(g);
     }
 
     @Test
-    public void dataSetReassignedFromExperimentToSampleWithExperiment()
+    public void containerWithSomeComponentsReassignedFromExperimentToSampleWithExperiment()
+    {
+        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1 DS2 DS4\n"
+                + "E2, samples: S2\n"
+                + "E3, data sets: DS3\n"
+                + "DS1, components: DS2 DS3\n"
+                + "DS2, components: DS4");
+        
+        reassignToSample(g.ds(1), g.s(2));
+        
+        assertEquals("E1, samples: S1\n"
+                + "E2, samples: S2, data sets: DS1 DS2 DS4\n"
+                + "E3, data sets: DS3\n"
+                + "S2, data sets: DS1 DS2 DS4\n"
+                + "DS1, components: DS2 DS3\n"
+                + "DS2, components: DS4\n", renderGraph(g));
+        repository.assertModified(g.e(1), g.e(2));
+        repository.assertModified(g.ds(1), g.ds(2), g.ds(4));
+        repository.assertModified(g.s(2));
+        repository.assertUnmodified(g);
+    }
+    
+    @Test
+    public void containerWithSomeComponentsReassignedFromExperimentToSampleWithExperiment2()
     {
-        EntityGraphGenerator g = parseAndCreateGraph("E1, data sets: DS1\nE2, samples: S2\n");
+        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1 DS2 DS4\n"
+                + "E2, samples: S2\n"
+                + "E3, data sets: DS3\n"
+                + "S1, data sets: DS2\n"
+                + "DS1, components: DS2 DS3\n"
+                + "DS2, components: DS4");
+        
+        reassignToSample(g.ds(1), g.s(2));
+        
+        assertEquals("E1, samples: S1, data sets: DS2 DS4\n"
+                + "E2, samples: S2, data sets: DS1\n"
+                + "E3, data sets: DS3\n"
+                + "S1, data sets: DS2\n"
+                + "S2, data sets: DS1\n"
+                + "DS1, components: DS2 DS3\n"
+                + "DS2, components: DS4\n", renderGraph(g));
+        repository.assertModified(g.e(1), g.e(2));
+        repository.assertModified(g.s(2));
+        repository.assertModified(g.ds(1));
+        repository.assertUnmodified(g);
+    }
+    
+    @Test
+    public void containerWithAllItsComponentsReassignedFromExperimentToExperiment()
+    {
+        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1 DS2\n"
+                + "E2, samples: S2\n"
+                + "DS1, components: DS2\n");
+        
+        reassignToExperiment(g.ds(1), g.e(2));
+        
+        assertEquals("E1, samples: S1\n"
+                + "E2, samples: S2, data sets: DS1 DS2\n"
+                + "DS1, components: DS2\n", renderGraph(g));
+        repository.assertModified(g.e(1), g.e(2));
+        repository.assertModified(g.ds(1), g.ds(2));
+        repository.assertUnmodified(g);
+    }
+    
+    @Test
+    public void containerWithAllItsComponentsReassignedFromExperimentToSampleWithExperiment()
+    {
+        EntityGraphGenerator g = parseAndCreateGraph("E1, data sets: DS1 DS2\n"
+                + "E2, samples: S2\n"
+                + "DS1, components: DS2\n");
 
         reassignToSample(g.ds(1), g.s(2));
 
-        assertEquals("E2, samples: S2, data sets: DS1\nS2, data sets: DS1\n", renderGraph(g));
+        assertEquals("E2, samples: S2, data sets: DS1 DS2\n"
+                + "S2, data sets: DS1 DS2\n"
+                + "DS1, components: DS2\n", renderGraph(g));
         repository.assertModified(g.e(1), g.e(2));
         repository.assertModified(g.s(2));
-        repository.assertModified(g.ds(1));
+        repository.assertModified(g.ds(1), g.ds(2));
+        repository.assertUnmodified(g);
     }
     
     @Test
-    public void dataSetReassignedFromExperimentToSampleWithoutExperiment()
+    public void containerWithAllItsComponentsReassignedFromExperimentToSampleWithoutExperiment()
     {
-        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1[NEXP-TYPE]\n"
-                + "S2\n");
+        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1[NECT] DS2[NET]\n"
+                + "S2\n"
+                + "DS1[NECT], components: DS2[NET]\n");
         
         reassignToSample(g.ds(1), g.s(2));
         
-        assertEquals("E1, samples: S1\nS2, data sets: DS1[NEXP-TYPE]\n", renderGraph(g));
+        assertEquals("E1, samples: S1\n"
+                + "S2, data sets: DS1[NECT] DS2[NET]\n"
+                + "DS1[NECT], components: DS2[NET]\n", renderGraph(g));
         repository.assertModified(g.e(1));
-        repository.assertUnmodified(g.s(1));
         repository.assertModified(g.s(2));
-        repository.assertModified(g.ds(1));
+        repository.assertModified(g.ds(1), g.ds(2));
+        repository.assertUnmodified(g);
     }
     
     @Test
-    public void dataSetReassignedFromSampleWithExperimentToExperiment()
+    public void containerWithAllItsComponentsReassignedFromSampleWithExperimentToExperiment()
     {
-        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1\n"
-                + "E2, samples: S2\nS1, data sets: DS1\n");
+        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1 DS2\n"
+                + "E2, samples: S2\n"
+                + "S1, data sets: DS1\n"
+                + "DS1, components: DS2");
 
         reassignToExperiment(g.ds(1), g.e(2));
 
         assertEquals("E1, samples: S1\n"
-                + "E2, samples: S2, data sets: DS1\n", renderGraph(g));
+                + "E2, samples: S2, data sets: DS1 DS2\n"
+                + "DS1, components: DS2\n", renderGraph(g));
         repository.assertModified(g.e(1), g.e(2));
         repository.assertModified(g.s(1));
-        repository.assertModified(g.ds(1));
-        repository.assertUnmodified(g.s(2));
+        repository.assertModified(g.ds(1), g.ds(2));
+        repository.assertUnmodified(g);
     }
     
     @Test
-    public void dataSetReassignedFromSampleWithExperimentToSampleWithExperiment()
+    public void containerWithAllItsComponentsReassignedFromSampleWithExperimentToSampleWithExperiment()
     {
-        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1\n"
-                + "E2, samples: S2\nS1, data sets: DS1\n");
+        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1 DS2\n"
+                + "E2, samples: S2\n"
+                + "S1, data sets: DS1\n"
+                + "DS1, components: DS2");
 
         reassignToSample(g.ds(1), g.s(2));
 
         assertEquals("E1, samples: S1\n"
-                + "E2, samples: S2, data sets: DS1\nS2, data sets: DS1\n", renderGraph(g));
+                + "E2, samples: S2, data sets: DS1 DS2\n"
+                + "S2, data sets: DS1\n"
+                + "DS1, components: DS2\n", renderGraph(g));
         repository.assertModified(g.e(1), g.e(2));
         repository.assertModified(g.s(1), g.s(2));
-        repository.assertModified(g.ds(1));
+        repository.assertModified(g.ds(1), g.ds(2));
+        repository.assertUnmodified(g);
     }
     
     @Test
-    public void dataSetReassignedFromSampleWithExperimentToSampleWithoutExperiment()
+    public void containerWithAllItsComponentsReassignedFromSampleWithExperimentToSampleWithoutExperiment()
     {
-        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1[NEXP-TYPE]\n"
-                + "S1, data sets: DS1[NEXP-TYPE]\nS2\n");
+        EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1, data sets: DS1[NECT] DS2[NET]\n"
+                + "S1, data sets: DS1[NECT] DS2[NET]\n"
+                + "S2\n"
+                + "DS1[NECT], components: DS2[NET]\n");
 
         reassignToSample(g.ds(1), g.s(2));
 
         assertEquals("E1, samples: S1\n"
-                + "S2, data sets: DS1[NEXP-TYPE]\n", renderGraph(g));
+                + "S2, data sets: DS1[NECT] DS2[NET]\n"
+                + "DS1[NECT], components: DS2[NET]\n", renderGraph(g));
         repository.assertModified(g.e(1));
         repository.assertModified(g.s(1), g.s(2));
-        repository.assertModified(g.ds(1));
+        repository.assertModified(g.ds(1), g.ds(2));
+        repository.assertUnmodified(g);
     }
 
     @Test
-    public void dataSetReassignedFromSampleWithoutExperimentToExperiment()
+    public void containerWithAllItsComponentsReassignedFromSampleWithoutExperimentToExperiment()
     {
         EntityGraphGenerator g = parseAndCreateGraph("E1\n"
-                + "S1, data sets: DS1[NEXP-TYPE]\n");
+                + "S1, data sets: DS1[NECT] DS2[NET]\n"
+                + "DS1[NECT], components: DS2[NET]\n");
 
         reassignToExperiment(g.ds(1), g.e(1));
 
-        assertEquals("E1, data sets: DS1[NEXP-TYPE]\n", renderGraph(g));
+        assertEquals("E1, data sets: DS1[NECT] DS2[NET]\n"
+                + "DS1[NECT], components: DS2[NET]\n", renderGraph(g));
         repository.assertModified(g.e(1));
         repository.assertModified(g.s(1));
-        repository.assertModified(g.ds(1));
+        repository.assertModified(g.ds(1), g.ds(2));
+        repository.assertUnmodified(g);
     }
     
     @Test
-    public void dataSetReassignedFromSampleWithoutExperimentToSampleWithExperiment()
+    public void containerWithAllItsComponentsReassignedFromSampleWithoutExperimentToSampleWithExperiment()
     {
         EntityGraphGenerator g = parseAndCreateGraph("E1, samples: S1\n"
-                + "S2, data sets: DS1[NEXP-TYPE]\n");
+                + "S2, data sets: DS1[NECT] DS2[NET]\n"
+                + "DS1[NECT], components: DS2[NET]\n");
 
         reassignToSample(g.ds(1), g.s(1));
 
-        assertEquals("E1, samples: S1, data sets: DS1[NEXP-TYPE]\n"
-                + "S1, data sets: DS1[NEXP-TYPE]\n", renderGraph(g));
+        assertEquals("E1, samples: S1, data sets: DS1[NECT] DS2[NET]\n"
+                + "S1, data sets: DS1[NECT] DS2[NET]\n"
+                + "DS1[NECT], components: DS2[NET]\n", renderGraph(g));
         repository.assertModified(g.e(1));
         repository.assertModified(g.s(1), g.s(2));
-        repository.assertModified(g.ds(1));
+        repository.assertModified(g.ds(1), g.ds(2));
+        repository.assertUnmodified(g);
     }
     
     @Test
-    public void dataSetReassignedFromSampleWithoutExperimentToSampleWithoutExperiment()
+    public void containerWithAllItsComponentsReassignedFromSampleWithoutExperimentToSampleWithoutExperiment()
     {
-        EntityGraphGenerator g = parseAndCreateGraph("S1, data sets: DS1[NO-EXP-TYPE]\n"
-                + "S2\n");
+        EntityGraphGenerator g = parseAndCreateGraph("S1, data sets: DS1[NECT] DS2[NET]\n"
+                + "S2\n"
+                + "DS1[NECT], components: DS2[NET]\n");
 
         reassignToSample(g.ds(1), g.s(2));
 
-        assertEquals("S2, data sets: DS1[NO-EXP-TYPE]\n", renderGraph(g));
+        assertEquals("S2, data sets: DS1[NECT] DS2[NET]\n"
+                + "DS1[NECT], components: DS2[NET]\n", renderGraph(g));
         repository.assertModified(g.s(1), g.s(2));
-        repository.assertModified(g.ds(1));
+        repository.assertModified(g.ds(1), g.ds(2));
+        repository.assertUnmodified(g);
     }
     
     @Test(expectedExceptions =
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java
index e2fb6d78a1195de85261e2cedcfd089f7685150e..570825d19f59ea83b953d72f45adb8572c0bb4e1 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/BaseTest.java
@@ -20,11 +20,17 @@ import static org.hamcrest.CoreMatchers.is;
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertNotNull;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
@@ -62,6 +68,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.AbstractExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.CodeWithRegistrationAndModificationDate;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ContainerDataSet;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseInstance;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Project;
@@ -165,7 +172,7 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
         System.setProperty("hibernate.search.index-mode", IndexMode.INDEX_FROM_SCRATCH.name());
         System.setProperty("hibernate.search.index-base", "../openbis/targets/lucene/cleandb");
         System.setProperty("hibernate.search.worker.execution", "sync");
-        System.setProperty(DataSetTypeWithoutExperimentChecker.PROPERTY_KEY, "  NO-EXP-.* ,   NEXP-.*  ");
+        System.setProperty(DataSetTypeWithoutExperimentChecker.PROPERTY_KEY, "  NO-EXP-.* ,   NE.*  ");
     }
 
     private void setContext() throws Exception
@@ -485,8 +492,9 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
     {
         EntityGraphGenerator g = new EntityGraphGenerator();
         g.parse(graphDefinition);
-        prepare(g);
-        assertEquals(removeSolitaryNodes(graphDefinition), renderGraph(g));
+        createGraph(g);
+        assertEquals(removeSolitaryNodes(graphDefinition), renderGraph(g, true));
+        repository.gatherModifictaionInfos();
         return g;
     }
     
@@ -506,31 +514,44 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
     
     protected String renderGraph(EntityGraphGenerator g)
     {
-        repository.refreshGraph();
+        return renderGraph(g, false);
+    }
+    
+    private String renderGraph(EntityGraphGenerator g, boolean showWhereIAm)
+    {
+        repository.refreshGraph(showWhereIAm);
         StringBuilder builder = new StringBuilder();
         for (ExperimentNode experimentNode : g.getExperiments().values())
         {
             StringBuilder builder2 = new StringBuilder();
             render(builder2, "samples", repository.getSampleNode(experimentNode));
             render(builder2, "data sets", repository.getDataSetNodes(experimentNode));
-            if (builder2.length() > 0)
-            {
-                builder.append(experimentNode.getCode());
-                builder.append(builder2).append("\n");
-            }
+            appendNodeTo(builder, experimentNode, builder2);
         }
         for (SampleNode sampleNode : g.getSamples().values())
         {
             StringBuilder builder2 = new StringBuilder();
             render(builder2, "data sets", repository.getDataSetNodes(sampleNode));
-            if (builder2.length() > 0)
-            {
-                builder.append(sampleNode.getCode());
-                builder.append(builder2).append("\n");
-            }
+            appendNodeTo(builder, sampleNode, builder2);
+        }
+        for (DataSetNode dataSetNode : g.getDataSets().values())
+        {
+            StringBuilder builder2 = new StringBuilder();
+            render(builder2, "components", repository.getComponentDataSetNodes(dataSetNode));
+            appendNodeTo(builder, dataSetNode, builder2);
         }
         return builder.toString();
     }
+
+    private void appendNodeTo(StringBuilder builder, EntityNode entityNode, StringBuilder builder2)
+    {
+        if (builder2.length() > 0)
+        {
+            builder.append(entityNode.getCodeAndType());
+            
+            builder.append(builder2).append("\n");
+        }
+    }
     
     private void render(StringBuilder builder, String name, Collection<? extends EntityNode> nodes)
     {
@@ -541,22 +562,18 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
         builder.append(", ").append(name).append(":");
         for (EntityNode node : nodes)
         {
-            builder.append(' ').append(node.getCode());
-            if (node.getType() != null)
-            {
-                builder.append("[").append(node.getType()).append("]");
-            }
+            builder.append(' ').append(node.getCodeAndType());
         }
     }
     
-    private void prepare(EntityGraphGenerator g)
+    private void createGraph(EntityGraphGenerator g)
     {
         repository = new EntityRepository();
         Space space = create(aSpace());
         Project project = create(aProject().inSpace(space));
         for (ExperimentNode experimentNode : g.getExperiments().values())
         {
-            repository.put(experimentNode, create(anExperiment().inProject(project)));
+            addToRepository(experimentNode, create(anExperiment().inProject(project)));
         }
         for (SampleNode sampleNode : g.getSamples().values())
         {
@@ -566,9 +583,11 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
             {
                 sample.inExperiment(repository.getExperiment(experimentNode));
             }
-            repository.put(sampleNode, create(sample));
+            addToRepository(sampleNode, sample);
         }
-        for (DataSetNode dataSetNode : g.getDataSets().values())
+        List<DataSetNode> dataSetNodes = new ArrayList<DataSetNode>(g.getDataSets().values());
+        Collections.reverse(dataSetNodes);
+        for (DataSetNode dataSetNode : dataSetNodes)
         {
             ExternalDataBuilder dataSet = aDataSet().withType(dataSetNode.getType());
             if (dataSetNode.getSample() != null)
@@ -578,10 +597,62 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
             {
                 dataSet.inExperiment(repository.getExperiment(dataSetNode.getExperiment()));
             }
+            List<DataSetNode> components = dataSetNode.getComponents();
+            if (components.isEmpty() == false)
+            {
+                dataSet.asContainer();
+                for (DataSetNode component : components)
+                {
+                    AbstractExternalData componentDataSet = repository.getDataSet(component);
+                    if (componentDataSet == null)
+                    {
+                        throw new IllegalStateException("Data set " + component.getCode() 
+                                + " is specified as component of " + dataSetNode.getCode() 
+                                + " but hasn't yet created.");
+                    }
+                    dataSet.withComponent(componentDataSet);
+                }
+            }
+            addToRepository(dataSetNode, dataSet);
+        }
+    }
+
+    private void addToRepository(ExperimentNode experimentNode, Experiment experiment)
+    {
+        try
+        {
+            repository.put(experimentNode, experiment);
+        } catch (Exception ex)
+        {
+            throw new RuntimeException("Error while creating experiment for node " + experimentNode.getCode() 
+                    + ": " + ex.getMessage(), ex);
+        }
+    }
+
+    private void addToRepository(SampleNode sampleNode, SampleBuilder sample)
+    {
+        try
+        {
+            repository.put(sampleNode, create(sample));
+        } catch (Exception ex)
+        {
+            throw new RuntimeException("Error while creating sample for node " + sampleNode.getCode() 
+                    + ": " + ex.getMessage(), ex);
+        }
+    }
+
+    private void addToRepository(DataSetNode dataSetNode, ExternalDataBuilder dataSet)
+    {
+        try
+        {
             repository.put(dataSetNode, create(dataSet));
+        } catch (Exception ex)
+        {
+            throw new RuntimeException("Error while creating data set for node " + dataSetNode.getCodeAndType() 
+                    + ": " + ex.getMessage(), ex);
         }
     }
-    
+
     public static ExperimentIdentifier id(Experiment experiment)
     {
         return new ExperimentIdentifier(id(experiment.getProject()), experiment.getCode());
@@ -680,18 +751,48 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
         private Map<Long, Experiment> experimentsNodeToDtoMap = new TreeMap<Long, Experiment>();
         private Map<Long, ExperimentNode> experimentDtoToNodeMap = new TreeMap<Long, ExperimentNode>();
         private Map<Long, ModificationInfo> experimentModificationInfoByNodeId = new HashMap<Long, ModificationInfo>();
+        private Set<ExperimentNode> modifiedExperimentNodes = new HashSet<ExperimentNode>();
         
         private Map<Long, Sample> samplesNodeToDtoMap = new TreeMap<Long, Sample>();
         private Map<Long, SampleNode> samplesDtoToNodeMap = new TreeMap<Long, SampleNode>();
         private Map<Long, ModificationInfo> sampleModificationInfoByNodeId = new HashMap<Long, ModificationInfo>();
+        private Set<SampleNode> modifiedSampleNodes = new HashSet<SampleNode>();
 
         private Map<Long, AbstractExternalData> dataSetsNodeToDtoMap = new TreeMap<Long, AbstractExternalData>();
         private Map<Long, DataSetNode> dataSetsDtoToNodeMap = new TreeMap<Long, DataSetNode>();
         private Map<Long, ModificationInfo> dataSetModificationInfoByNodeId = new HashMap<Long, ModificationInfo>();
-        
+        private Set<DataSetNode> modifiedDataSetNodes = new HashSet<DataSetNode>();
+
         private Map<Long, Set<Long>> experimentSamplesMap = new HashMap<Long, Set<Long>>();
         private Map<Long, Set<Long>> experimentDataSetsMap = new HashMap<Long, Set<Long>>();
         private Map<Long, Set<Long>> sampleDataSetsMap = new HashMap<Long, Set<Long>>();
+        private Map<Long, Set<Long>> componentDataSetsMap = new HashMap<Long, Set<Long>>();
+        
+        public String renderNodeToDtoMapping()
+        {
+            StringBuilder builder = new StringBuilder();
+            for (Entry<Long, Experiment> entry : experimentsNodeToDtoMap.entrySet())
+            {
+                render(builder, "E", entry.getKey(), entry.getValue());
+            }
+            for (Entry<Long, Sample> entry : samplesNodeToDtoMap.entrySet())
+            {
+                render(builder, "S", entry.getKey(), entry.getValue());
+            }
+            for (Entry<Long, AbstractExternalData> entry : dataSetsNodeToDtoMap.entrySet())
+            {
+                render(builder, "DS", entry.getKey(), entry.getValue());
+            }
+            return builder.toString();
+        }
+        
+        private void render(StringBuilder builder, String prefix, Long id, 
+                CodeWithRegistrationAndModificationDate<?> entity)
+        {
+            builder.append(prefix).append(id).append(" -> ").append(entity.getCode()).append(" (");
+            builder.append(entity.getModifier().getUserId()).append(", ");
+            builder.append(entity.getModificationDate()).append(")\n");
+        }
         
         public Experiment getExperiment(ExperimentNode experimentNode)
         {
@@ -708,19 +809,19 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
             return dataSetsNodeToDtoMap.get(dataSetNode.getId());
         }
         
-        void refreshGraph()
+        void refreshGraph(boolean showWhereIAm)
         {
             for (Long id : experimentsNodeToDtoMap.keySet())
             {
-                experimentsNodeToDtoMap.put(id,  refresh(experimentsNodeToDtoMap.get(id)));
+                experimentsNodeToDtoMap.put(id, refresh(experimentsNodeToDtoMap.get(id)));
             }
             for (Long id : samplesNodeToDtoMap.keySet())
             {
-                samplesNodeToDtoMap.put(id,  refresh(samplesNodeToDtoMap.get(id)));
+                samplesNodeToDtoMap.put(id, refresh(samplesNodeToDtoMap.get(id)));
             }
             for (Long id : dataSetsNodeToDtoMap.keySet())
             {
-                dataSetsNodeToDtoMap.put(id,  refresh(dataSetsNodeToDtoMap.get(id)));
+                dataSetsNodeToDtoMap.put(id, refresh(dataSetsNodeToDtoMap.get(id)));
             }
             experimentSamplesMap.clear();
             for (Sample sample : samplesNodeToDtoMap.values())
@@ -744,12 +845,34 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
                 addToDataSetsMap(experimentDataSetsMap, dataSet, dataSet.getExperiment());
             }
             sampleDataSetsMap.clear();
+            componentDataSetsMap.clear();
             for (AbstractExternalData dataSet : dataSetsNodeToDtoMap.values())
             {
                 addToDataSetsMap(sampleDataSetsMap, dataSet, dataSet.getSample());
+                List<ContainerDataSet> containerDataSets = dataSet.getContainerDataSets();
+                for (ContainerDataSet containerDataSet : containerDataSets)
+                {
+                    addToDataSetsMap(componentDataSetsMap, dataSet, containerDataSet);
+                }
             }
+            if (showWhereIAm)
+            {
+                printWhereIAm();
+            }
+            System.out.println("Entities mapping:\n" + renderNodeToDtoMapping());
         }
 
+        private void printWhereIAm()
+        {
+            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+            for (int i = 5, n = Math.min(stackTrace.length, 9); i < n; i++)
+            {
+                StackTraceElement element = stackTrace[i];
+                System.out.print(element.getMethodName() + " <- ");
+            }
+            System.out.println("...");
+        }
+        
         private void addToDataSetsMap(Map<Long, Set<Long>> holderDataSetsMap, 
                 AbstractExternalData dataSet, IIdHolder idHolder)
         {
@@ -766,57 +889,101 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
             }
         }
         
+        void gatherModifictaionInfos()
+        {
+            for (Entry<Long, Experiment> entry : experimentsNodeToDtoMap.entrySet())
+            {
+                experimentModificationInfoByNodeId.put(entry.getKey(), new ModificationInfo(entry.getValue()));
+            }
+            for (Entry<Long, Sample> entry : samplesNodeToDtoMap.entrySet())
+            {
+                sampleModificationInfoByNodeId.put(entry.getKey(), new ModificationInfo(entry.getValue()));
+            }
+            for (Entry<Long, AbstractExternalData> entry : dataSetsNodeToDtoMap.entrySet())
+            {
+                dataSetModificationInfoByNodeId.put(entry.getKey(), new ModificationInfo(entry.getValue()));
+            }
+        }
+        
         void put(ExperimentNode experimentNode, Experiment experiment)
         {
             experimentsNodeToDtoMap.put(experimentNode.getId(), experiment);
             experimentDtoToNodeMap.put(experiment.getId(), experimentNode);
-            experimentModificationInfoByNodeId.put(experimentNode.getId(), new ModificationInfo(experiment));
         }
         
         public void assertModified(ExperimentNode...experimentNodes)
         {
-            assertModificationInfo(true, experimentModificationInfoByNodeId, experimentsNodeToDtoMap, experimentNodes);
-        }
-        
-        public void assertUnmodified(ExperimentNode...experimentNodes)
-        {
-            assertModificationInfo(false, experimentModificationInfoByNodeId, experimentsNodeToDtoMap, experimentNodes);
+            assertModificationInfo(true, experimentModificationInfoByNodeId, experimentsNodeToDtoMap, 
+                    Arrays.<EntityNode>asList(experimentNodes));
+            modifiedExperimentNodes.addAll(Arrays.asList(experimentNodes));
         }
         
         void put(SampleNode sampleNode, Sample sample)
         {
             samplesNodeToDtoMap.put(sampleNode.getId(), sample);
             samplesDtoToNodeMap.put(sample.getId(), sampleNode);
-            sampleModificationInfoByNodeId.put(sampleNode.getId(), new ModificationInfo(sample));
         }
 
         public void assertModified(SampleNode...sampleNodes)
         {
-            assertModificationInfo(true, sampleModificationInfoByNodeId, samplesNodeToDtoMap, sampleNodes);
-        }
-        
-        public void assertUnmodified(SampleNode...sampleNodes)
-        {
-            assertModificationInfo(false, sampleModificationInfoByNodeId, samplesNodeToDtoMap, sampleNodes);
+            assertModificationInfo(true, sampleModificationInfoByNodeId, samplesNodeToDtoMap, 
+                    Arrays.<EntityNode>asList(sampleNodes));
+            modifiedSampleNodes.addAll(Arrays.asList(sampleNodes));
         }
         
         void put(DataSetNode dataSetNode, AbstractExternalData dataSet)
         {
             dataSetsNodeToDtoMap.put(dataSetNode.getId(), dataSet);
             dataSetsDtoToNodeMap.put(dataSet.getId(), dataSetNode);
-            dataSetModificationInfoByNodeId.put(dataSetNode.getId(), new ModificationInfo(dataSet));
         }
-
+        
         public void assertModified(DataSetNode...dataSetNodes)
         {
-            assertModificationInfo(true, dataSetModificationInfoByNodeId, dataSetsNodeToDtoMap, dataSetNodes);
+            assertModificationInfo(true, dataSetModificationInfoByNodeId, dataSetsNodeToDtoMap, 
+                    Arrays.<EntityNode>asList(dataSetNodes));
+            modifiedDataSetNodes.addAll(Arrays.asList(dataSetNodes));
         }
         
-        public void assertUnmodified(DataSetNode...dataSetNodes)
+        public void assertUnmodified(EntityGraphGenerator g)
         {
-            assertModificationInfo(false, dataSetModificationInfoByNodeId, dataSetsNodeToDtoMap, dataSetNodes);
+            Set<EntityNode> unmodifiedExperimentNodes = new HashSet<EntityNode>(g.getExperiments().values());
+            unmodifiedExperimentNodes.removeAll(modifiedExperimentNodes);
+            assertModificationInfo(false, experimentModificationInfoByNodeId, experimentsNodeToDtoMap, unmodifiedExperimentNodes);
+            Set<EntityNode> unmodifiedSampleNodes = new HashSet<EntityNode>(g.getSamples().values());
+            unmodifiedSampleNodes.removeAll(modifiedSampleNodes);
+            assertModificationInfo(false, sampleModificationInfoByNodeId, samplesNodeToDtoMap, unmodifiedSampleNodes);
+            Set<EntityNode> unmodifiedDataSetNodes = new HashSet<EntityNode>(g.getDataSets().values());
+            unmodifiedDataSetNodes.removeAll(modifiedDataSetNodes);
+            assertModificationInfo(false, dataSetModificationInfoByNodeId, dataSetsNodeToDtoMap, unmodifiedDataSetNodes);
         }
         
+        private void assertModificationInfo(boolean modified, Map<Long, ModificationInfo> previousInfos,
+                Map<Long, ? extends CodeWithRegistrationAndModificationDate<?>> nodeToDtoMap,
+                Collection<EntityNode> entityNodes)
+        {
+            for (EntityNode node : entityNodes)
+            {
+                ModificationInfo previous = previousInfos.get(node.getId());
+                assertNotNull(node.getCode() + " no previous modification info", previous);
+                CodeWithRegistrationAndModificationDate<?> entity = nodeToDtoMap.get(node.getId());
+                assertNotNull(node.getCode() + " unknown", entity);
+                ModificationInfo current = new ModificationInfo(entity);
+                if (modified)
+                {
+                    assertEquals(node.getCode() + " has unexpectedly still the old modifier: " + current.modifier, 
+                            false, current.modifier.equals(previous.modifier));
+                    assertEquals(node.getCode() + " has unexpectedly still the old modification date: " + current.modificationDate, 
+                            true, current.modificationDate.getTime() > previous.modificationDate.getTime());
+                } else
+                {
+                    assertEquals(node.getCode() + " has unexpectedly a new modifier: ",
+                            previous.modifier, current.modifier);
+                    assertEquals(node.getCode() + " has unexpectedly a new modification date:", 
+                            previous.modificationDate, current.modificationDate);
+                }
+            }
+        }
+
         Set<SampleNode> getSampleNode(ExperimentNode experimentNode)
         {
             Set<SampleNode> result = new LinkedHashSet<SampleNode>();
@@ -845,9 +1012,14 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
             return getDataSetNodes(sampleDataSetsMap, samplesNodeToDtoMap.get(sampleNode.getId()));
         }
         
+        Set<DataSetNode> getComponentDataSetNodes(DataSetNode containerDataSetNode)
+        {
+            return getDataSetNodes(componentDataSetsMap, dataSetsNodeToDtoMap.get(containerDataSetNode.getId()));
+        }
+        
         private Set<DataSetNode> getDataSetNodes(Map<Long, Set<Long>> idHolderDataSetsMap, IIdHolder experiment)
         {
-            Set<DataSetNode> result = new LinkedHashSet<DataSetNode>();
+            Set<DataSetNode> result = new TreeSet<DataSetNode>();
             if (experiment != null)
             {
                 Set<Long> dataSetDtoIds = idHolderDataSetsMap.get(experiment.getId());
@@ -861,33 +1033,6 @@ public abstract class BaseTest extends AbstractTransactionalTestNGSpringContextT
             }
             return result;
         }
-        
-        private void assertModificationInfo(boolean modified, Map<Long, ModificationInfo> previousInfos, 
-                Map<Long, ? extends CodeWithRegistrationAndModificationDate<?>> nodeToDtoMap, EntityNode...entityNodes)
-        {
-            for (EntityNode node : entityNodes)
-            {
-                ModificationInfo previous = previousInfos.get(node.getId());
-                assertNotNull(node.getCode() + " no previous modification info", previous);
-                CodeWithRegistrationAndModificationDate<?> entity = nodeToDtoMap.get(node.getId());
-                assertNotNull(node.getCode() + " unknown", entity);
-                ModificationInfo current = new ModificationInfo(entity);
-                if (modified)
-                {
-                    assertEquals(node.getCode() + " still same modifier: " + current.modifier, false, 
-                            current.modifier.equals(previous.modifier));
-                    assertEquals(node.getCode() + " still same modification date: " + current.modificationDate, 
-                            true, current.modificationDate.getTime() > previous.modificationDate.getTime());
-                } else
-                {
-                    assertEquals(node.getCode() + " different modifiers: " + previous.modifier + " " + previous.modifier,
-                            previous.modifier, current.modifier);
-                    assertEquals(node.getCode() + " different modification date: " + previous.modificationDate
-                            + " " + current.modificationDate, previous.modificationDate, current.modificationDate);
-                }
-            }
-        }
-        
     }
     
     private static final class ModificationInfo
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/builder/ExternalDataBuilder.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/builder/ExternalDataBuilder.java
index b15d7e0cfeccef056bd580ca21709054ed0b2c39..ecaacc7e6984a9f6745191ddbb8f008487a660bd 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/builder/ExternalDataBuilder.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/base/builder/ExternalDataBuilder.java
@@ -131,9 +131,13 @@ public class ExternalDataBuilder extends Builder<AbstractExternalData>
         if (this.sampleIdentifier != null)
         {
             etlService.registerDataSet(systemSession, sampleIdentifier, data);
-        } else
+        } else if (experimentIdentifier != null)
         {
             etlService.registerDataSet(systemSession, experimentIdentifier, data);
+        } else
+        {
+            throw new IllegalStateException("Neither sample nor experiment has been specified for data set "
+                    + data.getCode());
         }
 
         return etlService.tryGetDataSet(systemSession, this.code);
@@ -151,7 +155,7 @@ public class ExternalDataBuilder extends Builder<AbstractExternalData>
         {
             dataSetType.setDataSetKind(DataSetKind.PHYSICAL);
         }
-        commonServer.registerDataSetType(systemSession, dataSetType);
+        registerDataSetType(dataSetType);
 
         NewExternalData data;
         if (this.container)
@@ -175,4 +179,17 @@ public class ExternalDataBuilder extends Builder<AbstractExternalData>
         data.setParentDataSetCodes(this.parentCodes);
         return data;
     }
+
+    private void registerDataSetType(DataSetType dataSetType)
+    {
+        List<DataSetType> dataSetTypes = commonServer.listDataSetTypes(systemSession);
+        for (DataSetType type : dataSetTypes)
+        {
+            if (type.getCode().equals(dataSetType.getCode()))
+            {
+                return;
+            }
+        }
+        commonServer.registerDataSetType(systemSession, dataSetType);
+    }
 }
\ No newline at end of file