Repository: pydicom/pynetdicom
Pull request #374 (2019-07-13 04:11:12)

[MRG] Handle empty DIMSE message element values
#### Tasks
 - [x] Unit tests added that reproduce issue or prove feature is working
 - [x] Fix or feature added
 - [x] Documentation and examples updated (if relevant)
 - [x] Unit tests passing and coverage at 100% after adding fix/feature



Patch:
diff --git a/docs/changelog/v1.4.0.rst b/docs/changelog/v1.4.0.rst
index aadc5705c..b7da0b59c 100644
--- a/docs/changelog/v1.4.0.rst
+++ b/docs/changelog/v1.4.0.rst
@@ -48,6 +48,7 @@ Enhancements
 * Invalid (0000,1030) *Move Originator Application Entity Title* values
   received in C-STORE service requests are now logged and ignored rather than
   raising an exception and aborting the association.
+* Improved handling of DIMSE service messages with empty element values.
 
 
 Changes
diff --git a/pynetdicom/dimse_primitives.py b/pynetdicom/dimse_primitives.py
index 6d96b4edc..f40b07518 100644
--- a/pynetdicom/dimse_primitives.py
+++ b/pynetdicom/dimse_primitives.py
@@ -4,10 +4,6 @@ Define the DIMSE-C and DIMSE-N service parameter primitives.
 Notes:
   * The class member names must match their corresponding DICOM element keyword
     in order for the DIMSE messages/primitives to be created correctly.
-
-TODO: Implement properties for DIMSE-N parameters
-TODO: Implement status related parameters for DIMSE-N classes
-TODO: Add string output for the DIMSE-C classes
 """
 
 import codecs
@@ -39,7 +35,7 @@ class DIMSEPrimitive(object):
 
     @property
     def AffectedSOPClassUID(self):
-        """Return the *Affected SOP Class UID*."""
+        """Return the *Affected SOP Class UID* as a UID."""
         return self._affected_sop_class_uid
 
     @AffectedSOPClassUID.setter
@@ -49,7 +45,7 @@ class DIMSEPrimitive(object):
         Parameters
         ----------
         value : pydicom.uid.UID, bytes or str
-            The value for the Affected SOP Class UID
+            The value to use for the *Affected SOP Class UID* parameter.
         """
         if isinstance(value, UID):
             pass
@@ -63,7 +59,7 @@ class DIMSEPrimitive(object):
             raise TypeError("Affected SOP Class UID must be a "
                             "pydicom.uid.UID, str or bytes")
 
-        if value is not None and not validate_uid(value):
+        if value and not validate_uid(value):
             LOGGER.error("Affected SOP Class UID is an invalid UID")
             raise ValueError("Affected SOP Class UID is an invalid UID")
 
@@ -73,7 +69,51 @@ class DIMSEPrimitive(object):
                 .format(value)
             )
 
-        self._affected_sop_class_uid = value
+        if value:
+            self._affected_sop_class_uid = value
+        else:
+            self._affected_sop_class_uid = None
+
+    @property
+    def _AffectedSOPInstanceUID(self):
+        """Return the *Affected SOP Instance UID*."""
+        return self._affected_sop_instance_uid
+
+    @_AffectedSOPInstanceUID.setter
+    def _AffectedSOPInstanceUID(self, value):
+        """Set the *Affected SOP Instance UID*.
+
+        Parameters
+        ----------
+        value : pydicom.uid.UID, bytes or str
+            The value for the Affected SOP Class UID
+        """
+        if isinstance(value, UID):
+            pass
+        elif isinstance(value, str):
+            value = UID(value)
+        elif isinstance(value, bytes):
+            value = UID(value.decode('ascii'))
+        elif value is None:
+            pass
+        else:
+            raise TypeError("Affected SOP Instance UID must be a "
+                            "pydicom.uid.UID, str or bytes")
+
+        if value and not validate_uid(value):
+            LOGGER.error("Affected SOP Instance UID is an invalid UID")
+            raise ValueError("Affected SOP Instance UID is an invalid UID")
+
+        if value and not value.is_valid:
+            LOGGER.warning(
+                "The Affected SOP Instance UID '{}' is non-conformant"
+                .format(value)
+            )
+
+        if value:
+            self._affected_sop_instance_uid = value
+        else:
+            self._affected_sop_instance_uid = None
 
     @property
     def _dataset_variant(self):
@@ -132,12 +172,18 @@ class DIMSEPrimitive(object):
 
     @property
     def MessageID(self):
-        """Return the DIMSE *Message ID*."""
+        """Return the *Message ID* value as an int."""
         return self._message_id
 
     @MessageID.setter
     def MessageID(self, value):
-        """Set the DIMSE *Message ID*."""
+        """Set the *Message ID*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Message ID* parameter.
+        """
         if isinstance(value, int):
             if 0 <= value < 2**16:
                 self._message_id = value
@@ -151,12 +197,18 @@ class DIMSEPrimitive(object):
 
     @property
     def MessageIDBeingRespondedTo(self):
-        """Return the *Message ID Being Responded To*."""
+        """Return the *Message ID Being Responded To* as an int."""
         return self._message_id_being_responded_to
 
     @MessageIDBeingRespondedTo.setter
     def MessageIDBeingRespondedTo(self, value):
-        """Set the *Message ID Being Responded To*."""
+        """Set the *Message ID Being Responded To*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Message ID Being Responded To* parameter.
+        """
         if isinstance(value, int):
             if 0 <= value < 2**16:
                 self._message_id_being_responded_to = value
@@ -168,14 +220,193 @@ class DIMSEPrimitive(object):
         else:
             raise TypeError("Message ID Being Responded To must be an int")
 
+    @property
+    def _NumberOfCompletedSuboperations(self):
+        """Return the *Number of Completed Suboperations*."""
+        return self._number_of_completed_suboperations
+
+    @_NumberOfCompletedSuboperations.setter
+    def _NumberOfCompletedSuboperations(self, value):
+        """Set the *Number of Completed Suboperations*."""
+        if isinstance(value, int):
+            if value >= 0:
+                self._number_of_completed_suboperations = value
+            else:
+                raise ValueError("Number of Completed Suboperations must be "
+                                 "greater than or equal to 0")
+        elif value is None:
+            self._number_of_completed_suboperations = value
+        else:
+            raise TypeError("Number of Completed Suboperations must be an int")
+
+    @property
+    def _NumberOfFailedSuboperations(self):
+        """Return the *Number of Failed Suboperations*."""
+        return self._number_of_failed_suboperations
+
+    @_NumberOfFailedSuboperations.setter
+    def _NumberOfFailedSuboperations(self, value):
+        """Set the *Number of Failed Suboperations*."""
+        if isinstance(value, int):
+            if value >= 0:
+                self._number_of_failed_suboperations = value
+            else:
+                raise ValueError("Number of Failed Suboperations must be "
+                                 "greater than or equal to 0")
+        elif value is None:
+            self._number_of_failed_suboperations = value
+        else:
+            raise TypeError("Number of Failed Suboperations must be an int")
+
+    @property
+    def _NumberOfRemainingSuboperations(self):
+        """Return the *Number of Remaining Suboperations*."""
+        return self._number_of_remaining_suboperations
+
+    @_NumberOfRemainingSuboperations.setter
+    def _NumberOfRemainingSuboperations(self, value):
+        """Set the *Number of Remaining Suboperations*."""
+        if isinstance(value, int):
+            if value >= 0:
+                self._number_of_remaining_suboperations = value
+            else:
+                raise ValueError("Number of Remaining Suboperations must be "
+                                 "greater than or equal to 0")
+        elif value is None:
+            self._number_of_remaining_suboperations = value
+        else:
+            raise TypeError("Number of Remaining Suboperations must be an int")
+
+    @property
+    def _NumberOfWarningSuboperations(self):
+        """Return the *Number of Warning Suboperations*."""
+        return self._number_of_warning_suboperations
+
+    @_NumberOfWarningSuboperations.setter
+    def _NumberOfWarningSuboperations(self, value):
+        """Set the *Number of Warning Suboperations*."""
+        if isinstance(value, int):
+            if value >= 0:
+                self._number_of_warning_suboperations = value
+            else:
+                raise ValueError("Number of Warning Suboperations must be "
+                                 "greater than or equal to 0")
+        elif value is None:
+            self._number_of_warning_suboperations = value
+        else:
+            raise TypeError("Number of Warning Suboperations must be an int")
+
+    @property
+    def _Priority(self):
+        """Return the *Priority*."""
+        return self._priority
+
+    @_Priority.setter
+    def _Priority(self, value):
+        """Set the *Priority*."""
+        if value in [0, 1, 2]:
+            self._priority = value
+        else:
+            LOGGER.warning("Attempted to set Priority parameter to "
+                           "an invalid value")
+            raise ValueError("Priority must be 0, 1, or 2")
+
+    @property
+    def _RequestedSOPClassUID(self):
+        """Return the *Requested SOP Class UID*."""
+        return self._requested_sop_class_uid
+
+    @_RequestedSOPClassUID.setter
+    def _RequestedSOPClassUID(self, value):
+        """Set the *Requested SOP Class UID*.
+
+        Parameters
+        ----------
+        value : pydicom.uid.UID, bytes or str
+            The value for the Requested SOP Class UID
+        """
+        if isinstance(value, UID):
+            pass
+        elif isinstance(value, str):
+            value = UID(value)
+        elif isinstance(value, bytes):
+            value = UID(value.decode('ascii'))
+        elif value is None:
+            pass
+        else:
+            raise TypeError("Requested SOP Class UID must be a "
+                            "pydicom.uid.UID, str or bytes")
+
+        if value and not validate_uid(value):
+            LOGGER.error("Requested SOP Class UID is an invalid UID")
+            raise ValueError("Requested SOP Class UID is an invalid UID")
+
+        if value and not value.is_valid:
+            LOGGER.warning(
+                "The Requested SOP Class UID '{}' is non-conformant"
+                .format(value)
+            )
+
+        if value:
+            self._requested_sop_class_uid = value
+        else:
+            self._requested_sop_class_uid = None
+
+    @property
+    def _RequestedSOPInstanceUID(self):
+        """Return the *Requested SOP Instance UID*."""
+        return self._requested_sop_instance_uid
+
+    @_RequestedSOPInstanceUID.setter
+    def _RequestedSOPInstanceUID(self, value):
+        """Set the *Requested SOP Instance UID*.
+
+        Parameters
+        ----------
+        value : pydicom.uid.UID, bytes or str
+            The value for the Requested SOP Instance UID
+        """
+        if isinstance(value, UID):
+            pass
+        elif isinstance(value, str):
+            value = UID(value)
+        elif isinstance(value, bytes):
+            value = UID(value.decode('ascii'))
+        elif value is None:
+            pass
+        else:
+            raise TypeError("Requested SOP Instance UID must be a "
+                            "pydicom.uid.UID, str or bytes")
+
+        if value and not validate_uid(value):
+            LOGGER.error("Requested SOP Instance UID is an invalid UID")
+            raise ValueError("Requested SOP Instance UID is an invalid UID")
+
+        if value and not value.is_valid:
+            LOGGER.warning(
+                "The Requested SOP Instance UID '{}' is non-conformant"
+                .format(value)
+            )
+
+        if value:
+            self._requested_sop_instance_uid = value
+        else:
+            self._requested_sop_instance_uid = None
+
     @property
     def Status(self):
-        """Return the *Status*."""
+        """Return the *Status* as an int."""
         return self._status
 
     @Status.setter
     def Status(self, value):
-        """Set the *Status*."""
+        """Set the *Status*
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Status* parameter.
+        """
         if isinstance(value, int) or value is None:
             self._status = value
         else:
@@ -297,8 +528,8 @@ class C_STORE(DIMSEPrimitive):
 
     @property
     def AffectedSOPInstanceUID(self):
-        """Return the *Affected SOP Instance UID*."""
-        return self._affected_sop_instance_uid
+        """Return the *Affected SOP Instance UID* as UID."""
+        return self._AffectedSOPInstanceUID
 
     @AffectedSOPInstanceUID.setter
     def AffectedSOPInstanceUID(self, value):
@@ -307,45 +538,29 @@ class C_STORE(DIMSEPrimitive):
         Parameters
         ----------
         value : pydicom.uid.UID, bytes or str
-            The value for the Affected SOP Class UID
+            The value to use for the *Affected SOP Class UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Affected SOP Instance UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Affected SOP Instance UID is an invalid UID")
-            raise ValueError("Affected SOP Instance UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Affected SOP Instance UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._affected_sop_instance_uid = value
+        self._AffectedSOPInstanceUID = value
 
     @property
     def DataSet(self):
-        """Return the *Data Set*."""
+        """Return the *Data Set* as io.BytesIO."""
         return self._dataset_variant
 
     @DataSet.setter
     def DataSet(self, value):
-        """Set the *Data Set*."""
+        """Set the *Data Set*.
+
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Data Set* parameter.
+        """
         self._dataset_variant = (value, 'DataSet')
 
     @property
     def MoveOriginatorApplicationEntityTitle(self):
-        """Return the *Move Originator Application Entity Title*."""
+        """Return the *Move Originator Application Entity Title* as bytes."""
         return self._move_originator_application_entity_title
 
     @MoveOriginatorApplicationEntityTitle.setter
@@ -354,10 +569,10 @@ class C_STORE(DIMSEPrimitive):
 
         Parameters
         ----------
-        value : str or bytes
-            The Move Originator AE Title as a string or bytes object. Cannot be
-            an empty string and will be truncated to 16 characters long.
-            Invalid values will be ignored.
+        bytes or str
+            The value to use for the *Move Originator AE Title* parameter.
+            The parameter value will be truncated to 16 bytes and invalid
+            values ignored.
         """
         if isinstance(value, str):
             value = codecs.encode(value, 'ascii')
@@ -378,12 +593,18 @@ class C_STORE(DIMSEPrimitive):
 
     @property
     def MoveOriginatorMessageID(self):
-        """Return the *Move Originator Message ID*."""
+        """Return the *Move Originator Message ID* as int."""
         return self._move_originator_message_id
 
     @MoveOriginatorMessageID.setter
     def MoveOriginatorMessageID(self, value):
-        """Set the *Move Originator Message ID*."""
+        """Set the *Move Originator Message ID*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Move Originator Message ID* parameter.
+        """
         # Fix for peers sending a value consisting of nulls
         if isinstance(value, int):
             if 0 <= value < 2**16:
@@ -398,18 +619,19 @@ class C_STORE(DIMSEPrimitive):
 
     @property
     def Priority(self):
-        """Return the *Priority*."""
-        return self._priority
+        """Return the *Priority* as an int."""
+        return self._Priority
 
     @Priority.setter
     def Priority(self, value):
-        """Set the *Priority*."""
-        if value in [0, 1, 2]:
-            self._priority = value
-        else:
-            LOGGER.warning("Attempted to set C-STORE Priority parameter to "
-                           "an invalid value")
-            raise ValueError("C-STORE Priority must be 0, 1, or 2")
+        """Set the *Priority*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Priority* parameter.
+        """
+        self._Priority = value
 
 
 class C_FIND(DIMSEPrimitive):
@@ -503,28 +725,35 @@ class C_FIND(DIMSEPrimitive):
 
     @property
     def Identifier(self):
-        """Return the *Identifier*."""
+        """Return the *Identifier* as io.BytesIO."""
         return self._dataset_variant
 
     @Identifier.setter
     def Identifier(self, value):
-        """Set the *Identifier*."""
+        """Set the *Identifier*.
+
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Identifier* parameter.
+        """
         self._dataset_variant = (value, 'Identifier')
 
     @property
     def Priority(self):
-        """Return the *Priority*."""
-        return self._priority
+        """Return the *Priority* as an int."""
+        return self._Priority
 
     @Priority.setter
     def Priority(self, value):
-        """Set the *Priority*."""
-        if value in [0, 1, 2]:
-            self._priority = value
-        else:
-            LOGGER.warning("Attempted to set C-FIND Priority parameter to an "
-                           "invalid value")
-            raise ValueError("Priority must be 0, 1, or 2")
+        """Set the *Priority*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Priority* parameter.
+        """
+        self._Priority = value
 
 
 class C_GET(DIMSEPrimitive):
@@ -653,104 +882,103 @@ class C_GET(DIMSEPrimitive):
 
     @property
     def Identifier(self):
-        """Return the *Identifier*."""
+        """Return the *Identifier* as io.BytesIO."""
         return self._dataset_variant
 
     @Identifier.setter
     def Identifier(self, value):
-        """Set the *Identifier*."""
-        self._dataset_variant = (value, 'Identifier')
+        """Set the *Identifier*.
 
-    @property
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Identifier* parameter.
+        """
+        self._dataset_variant = (value, 'Identifier')
+
+    @property
     def NumberOfCompletedSuboperations(self):
-        """Return the *Number of Completed Suboperations*."""
-        return self._number_of_completed_suboperations
+        """Return the *Number of Completed Suboperations* as int."""
+        return self._NumberOfCompletedSuboperations
 
     @NumberOfCompletedSuboperations.setter
     def NumberOfCompletedSuboperations(self, value):
-        """Set the *Number of Completed Suboperations*."""
-        if isinstance(value, int):
-            if value >= 0:
-                self._number_of_completed_suboperations = value
-            else:
-                raise ValueError("Number of Completed Suboperations must be "
-                                 "greater than or equal to 0")
-        elif value is None:
-            self._number_of_completed_suboperations = value
-        else:
-            raise TypeError("Number of Completed Suboperations must be an int")
+        """Set the *Number of Completed Suboperations*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Number of Completed Suboperations*
+            parameter.
+        """
+        self._NumberOfCompletedSuboperations = value
 
     @property
     def NumberOfFailedSuboperations(self):
-        """Return the *Number of Failed Suboperations*."""
-        return self._number_of_failed_suboperations
+        """Return the *Number of Failed Suboperations* as int."""
+        return self._NumberOfFailedSuboperations
 
     @NumberOfFailedSuboperations.setter
     def NumberOfFailedSuboperations(self, value):
-        """Set the *Number of Failed Suboperations*."""
-        if isinstance(value, int):
-            if value >= 0:
-                self._number_of_failed_suboperations = value
-            else:
-                raise ValueError("Number of Failed Suboperations must be "
-                                 "greater than or equal to 0")
-        elif value is None:
-            self._number_of_failed_suboperations = value
-        else:
-            raise TypeError("Number of Failed Suboperations must be an int")
+        """Set the *Number of Failed Suboperations*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Number of Failed Suboperations*
+            parameter.
+        """
+        self._NumberOfFailedSuboperations = value
 
     @property
     def NumberOfRemainingSuboperations(self):
-        """Return the *Number of Remaining Suboperations*."""
-        return self._number_of_remaining_suboperations
+        """Return the *Number of Remaining Suboperations* as int."""
+        return self._NumberOfRemainingSuboperations
 
     @NumberOfRemainingSuboperations.setter
     def NumberOfRemainingSuboperations(self, value):
-        """Set the *Number of Remaining Suboperations*."""
-        if isinstance(value, int):
-            if value >= 0:
-                self._number_of_remaining_suboperations = value
-            else:
-                raise ValueError("Number of Remaining Suboperations must be "
-                                 "greater than or equal to 0")
-        elif value is None:
-            self._number_of_remaining_suboperations = value
-        else:
-            raise TypeError("Number of Remaining Suboperations must be an int")
+        """Set the *Number of Remaining Suboperations*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Number of Remaining Suboperations*
+            parameter.
+        """
+        self._NumberOfRemainingSuboperations = value
 
     @property
     def NumberOfWarningSuboperations(self):
-        """Return the *Number of Warning Suboperations*."""
-        return self._number_of_warning_suboperations
+        """Return the *Number of Warning Suboperations* as int."""
+        return self._NumberOfWarningSuboperations
 
     @NumberOfWarningSuboperations.setter
     def NumberOfWarningSuboperations(self, value):
-        """Set the *Number of Warning Suboperations*."""
-        if isinstance(value, int):
-            if value >= 0:
-                self._number_of_warning_suboperations = value
-            else:
-                raise ValueError("Number of Warning Suboperations must be "
-                                 "greater than or equal to 0")
-        elif value is None:
-            self._number_of_warning_suboperations = value
-        else:
-            raise TypeError("Number of Warning Suboperations must be an int")
+        """Set the *Number of Warning Suboperations*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Number of Warning Suboperations*
+            parameter.
+        """
+        self._NumberOfWarningSuboperations = value
 
     @property
     def Priority(self):
-        """Return the *Priority*."""
-        return self._priority
+        """Return the *Priority* as an int."""
+        return self._Priority
 
     @Priority.setter
     def Priority(self, value):
-        """Set the *Priority*."""
-        if value in [0, 1, 2]:
-            self._priority = value
-        else:
-            LOGGER.warning("Attempted to set C-FIND Priority parameter to an "
-                           "invalid value")
-            raise ValueError("Priority must be 0, 1, or 2")
+        """Set the *Priority*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Priority* parameter.
+        """
+        self._Priority = value
 
 
 class C_MOVE(DIMSEPrimitive):
@@ -884,17 +1112,23 @@ class C_MOVE(DIMSEPrimitive):
 
     @property
     def Identifier(self):
-        """Return the *Identifier*."""
+        """Return the *Identifier* as io.BytesIO."""
         return self._dataset_variant
 
     @Identifier.setter
     def Identifier(self, value):
-        """Set the *Identifier*."""
+        """Set the *Identifier*.
+
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Identifier* parameter.
+        """
         self._dataset_variant = (value, 'Identifier')
 
     @property
     def MoveDestination(self):
-        """Return the *Move Destination*."""
+        """Return the *Move Destination* as bytes."""
         return self._move_destination
 
     @MoveDestination.setter
@@ -903,8 +1137,8 @@ class C_MOVE(DIMSEPrimitive):
 
         Parameters
         ----------
-        value : str or bytes
-            The Move Destination AE Title as a string or bytes object. Cannot
+        bytes or str
+            The value to use for the *Move Destination* parameter. Cannot
             be an empty string and will be truncated to 16 characters long
         """
         if isinstance(value, str):
@@ -917,94 +1151,87 @@ class C_MOVE(DIMSEPrimitive):
 
     @property
     def NumberOfCompletedSuboperations(self):
-        """Return the *Number of Completed Suboperations*."""
-        return self._number_of_completed_suboperations
+        """Return the *Number of Completed Suboperations* as int."""
+        return self._NumberOfCompletedSuboperations
 
     @NumberOfCompletedSuboperations.setter
     def NumberOfCompletedSuboperations(self, value):
-        """Set the *Number of Completed Suboperations*."""
-        if isinstance(value, int):
-            if value >= 0:
-                self._number_of_completed_suboperations = value
-            else:
-                raise ValueError("Number of Completed Suboperations must be "
-                                 "greater than or equal to 0")
-        elif value is None:
-            self._number_of_completed_suboperations = value
-        else:
-            raise TypeError("Number of Completed Suboperations must be an int")
+        """Set the *Number of Completed Suboperations*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Number of Completed Suboperations*
+            parameter.
+        """
+        self._NumberOfCompletedSuboperations = value
 
     @property
     def NumberOfFailedSuboperations(self):
-        """Return the *Number of Failed Suboperations*."""
-        return self._number_of_failed_suboperations
+        """Return the *Number of Failed Suboperations* as int."""
+        return self._NumberOfFailedSuboperations
 
     @NumberOfFailedSuboperations.setter
     def NumberOfFailedSuboperations(self, value):
-        """Set the *Number of Failed Suboperations*."""
-        if isinstance(value, int):
-            if value >= 0:
-                self._number_of_failed_suboperations = value
-            else:
-                raise ValueError("Number of Failed Suboperations must be "
-                                 "greater than or equal to 0")
-        elif value is None:
-            self._number_of_failed_suboperations = value
-        else:
-            raise TypeError("Number of Failed Suboperations must be an int")
+        """Set the *Number of Failed Suboperations*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Number of Failed Suboperations*
+            parameter.
+        """
+        self._NumberOfFailedSuboperations = value
 
     @property
     def NumberOfRemainingSuboperations(self):
-        """Return the *Number of Remaining Suboperations*."""
-        return self._number_of_remaining_suboperations
+        """Return the *Number of Remaining Suboperations* as int."""
+        return self._NumberOfRemainingSuboperations
 
     @NumberOfRemainingSuboperations.setter
     def NumberOfRemainingSuboperations(self, value):
-        """Set the *Number of Remaining Suboperations*."""
-        if isinstance(value, int):
-            if value >= 0:
-                self._number_of_remaining_suboperations = value
-            else:
-                raise ValueError("Number of Remaining Suboperations must be "
-                                 "greater than or equal to 0")
-        elif value is None:
-            self._number_of_remaining_suboperations = value
-        else:
-            raise TypeError("Number of Remaining Suboperations must be an int")
+        """Set the *Number of Remaining Suboperations*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Number of Remaining Suboperations*
+            parameter.
+        """
+        self._NumberOfRemainingSuboperations = value
 
     @property
     def NumberOfWarningSuboperations(self):
-        """Return the *Number of Warning Suboperations*."""
-        return self._number_of_warning_suboperations
+        """Return the *Number of Warning Suboperations* as int."""
+        return self._NumberOfWarningSuboperations
 
     @NumberOfWarningSuboperations.setter
     def NumberOfWarningSuboperations(self, value):
-        """Set the *Number of Warning Suboperations*."""
-        if isinstance(value, int):
-            if value >= 0:
-                self._number_of_warning_suboperations = value
-            else:
-                raise ValueError("Number of Warning Suboperations must be "
-                                 "greater than or equal to 0")
-        elif value is None:
-            self._number_of_warning_suboperations = value
-        else:
-            raise TypeError("Number of Warning Suboperations must be an int")
+        """Set the *Number of Warning Suboperations*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Number of Warning Suboperations*
+            parameter.
+        """
+        self._NumberOfWarningSuboperations = value
 
     @property
     def Priority(self):
-        """Return the *Priority*."""
-        return self._priority
+        """Return the *Priority* as an int."""
+        return self._Priority
 
     @Priority.setter
     def Priority(self, value):
-        """Set the *Priority*."""
-        if value in [0, 1, 2]:
-            self._priority = value
-        else:
-            LOGGER.warning("Attempted to set C-FIND Priority parameter to an "
-                           "invalid value")
-            raise ValueError("Priority must be 0, 1, or 2")
+        """Set the *Priority*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Priority* parameter.
+        """
+        self._Priority = value
 
 
 class C_ECHO(DIMSEPrimitive):
@@ -1109,12 +1336,18 @@ class C_CANCEL(object):
 
     @property
     def MessageIDBeingRespondedTo(self):
-        """Return the *Message ID Being Responded To*."""
+        """Return the *Message ID Being Responded To* as an int."""
         return self._message_id_being_responded_to
 
     @MessageIDBeingRespondedTo.setter
     def MessageIDBeingRespondedTo(self, value):
-        """Set the *Message ID Being Responded To*."""
+        """Set the *Message ID Being Responded To*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Message ID Being Responded To* parameter.
+        """
         if isinstance(value, int):
             if 0 <= value < 2**16:
                 self._message_id_being_responded_to = value
@@ -1127,7 +1360,6 @@ class C_CANCEL(object):
             raise TypeError("Message ID Being Responded To must be an int")
 
 
-
 # DIMSE-N Service Primitives
 class N_EVENT_REPORT(DIMSEPrimitive):
     """Represents a N-EVENT-REPORT primitive.
@@ -1217,8 +1449,8 @@ class N_EVENT_REPORT(DIMSEPrimitive):
 
     @property
     def AffectedSOPInstanceUID(self):
-        """Return the *Affected SOP Instance UID*."""
-        return self._affected_sop_instance_uid
+        """Return the *Affected SOP Instance UID* as UID."""
+        return self._AffectedSOPInstanceUID
 
     @AffectedSOPInstanceUID.setter
     def AffectedSOPInstanceUID(self, value):
@@ -1227,60 +1459,56 @@ class N_EVENT_REPORT(DIMSEPrimitive):
         Parameters
         ----------
         value : pydicom.uid.UID, bytes or str
-            The value for the Affected SOP Instance UID
+            The value to use for the *Affected SOP Class UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Affected SOP Instance UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Affected SOP Instance UID is an invalid UID")
-            raise ValueError("Affected SOP Instance UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Affected SOP Instance UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._affected_sop_instance_uid = value
+        self._AffectedSOPInstanceUID = value
 
     @property
     def EventInformation(self):
-        """Return the *Event Information*."""
+        """Return the *Event Information* as io.BytesIO."""
         return self._dataset_variant
 
     @EventInformation.setter
     def EventInformation(self, value):
-        """Set the *Event Information*."""
+        """Set the *Event Information*.
+
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Event Information* parameter.
+        """
         self._dataset_variant = (value, 'EventInformation')
 
     @property
     def EventReply(self):
-        """Return the *Event Reply*."""
+        """Return the *Event Reply* as io.BytesIO."""
         return self._dataset_variant
 
     @EventReply.setter
     def EventReply(self, value):
-        """Set the *Event Reply*."""
+        """Set the *Event Reply*.
+
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Event Reply* parameter.
+        """
         self._dataset_variant = (value, 'EventReply')
 
     @property
     def EventTypeID(self):
-        """Return the *Event Type ID*."""
+        """Return the *Event Type ID* as int."""
         return self._event_type_id
 
     @EventTypeID.setter
     def EventTypeID(self, value):
-        """Set the *Event Type ID*."""
+        """Set the *Event Type ID*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Event Type ID* parameter.
+        """
         if isinstance(value, int) or value is None:
             self._event_type_id = value
         else:
@@ -1371,8 +1599,8 @@ class N_GET(DIMSEPrimitive):
 
     @property
     def AffectedSOPInstanceUID(self):
-        """Return the *Affected SOP Instance UID*."""
-        return self._affected_sop_instance_uid
+        """Return the *Affected SOP Instance UID* as UID."""
+        return self._AffectedSOPInstanceUID
 
     @AffectedSOPInstanceUID.setter
     def AffectedSOPInstanceUID(self, value):
@@ -1381,46 +1609,28 @@ class N_GET(DIMSEPrimitive):
         Parameters
         ----------
         value : pydicom.uid.UID, bytes or str
-            The value for the Affected SOP Instance UID
+            The value to use for the *Affected SOP Class UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Affected SOP Instance UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Affected SOP Instance UID is an invalid UID")
-            raise ValueError("Affected SOP Instance UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Affected SOP Instance UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._affected_sop_instance_uid = value
+        self._AffectedSOPInstanceUID = value
 
     @property
     def AttributeIdentifierList(self):
-        """Return the value of (0000,1005) *Attribute Identifier List*."""
+        """Return the *Attribute Identifier List* as a list of
+        pydicom.tag.Tag.
+
+        """
         return self._attribute_identifier_list
 
     @AttributeIdentifierList.setter
     def AttributeIdentifierList(self, value):
-        """Set the value of (0000,1005) *Attribute Identifier List*.
+        """Set the *Attribute Identifier List*.
 
         Parameters
         ----------
-        value : list of pydicom.tag.Tag
-            A list of pydicom Tags or any values acceptable for creating a new
-            pydicom Tag object.
+        list of pydicom.tag.Tag
+            The value to use for the *Attribute Identifier List* parameter.
+            A list of pydicom ``Tag`` instances or any values acceptable for
+            creating a ``Tag`` instance.
         """
         if value is None:
             self._attribute_identifier_list = None
@@ -1445,18 +1655,24 @@ class N_GET(DIMSEPrimitive):
 
     @property
     def AttributeList(self):
-        """Return the *Attribute List*."""
+        """Return the *Attribute List* as io.BytesIO."""
         return self._dataset_variant
 
     @AttributeList.setter
     def AttributeList(self, value):
-        """Set the *Attribute List*."""
+        """Set the *Attribute List*.
+
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Attribute List* parameter.
+        """
         self._dataset_variant = (value, 'AttributeList')
 
     @property
     def RequestedSOPClassUID(self):
-        """Return the *Requested SOP Class UID*."""
-        return self._requested_sop_class_uid
+        """Return the *Requested SOP Class UID* as UID."""
+        return self._RequestedSOPClassUID
 
     @RequestedSOPClassUID.setter
     def RequestedSOPClassUID(self, value):
@@ -1464,37 +1680,15 @@ class N_GET(DIMSEPrimitive):
 
         Parameters
         ----------
-        value : pydicom.uid.UID, bytes or str
-            The value for the Requested SOP Class UID
+        pydicom.uid.UID, bytes or str
+            The value to use for the *Requested SOP Class UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Requested SOP Class UID must be a "
-                            "pydicom.uid.UID, str or bytes")
+        self._RequestedSOPClassUID = value
 
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Requested SOP Class UID is an invalid UID")
-            raise ValueError("Requested SOP Class UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Requested SOP Class UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._requested_sop_class_uid = value
-
-    @property
-    def RequestedSOPInstanceUID(self):
-        """Return the *Requested SOP Instance UID*."""
-        return self._requested_sop_instance_uid
+    @property
+    def RequestedSOPInstanceUID(self):
+        """Return the *Requested SOP Instance UID* as UID."""
+        return self._RequestedSOPInstanceUID
 
     @RequestedSOPInstanceUID.setter
     def RequestedSOPInstanceUID(self, value):
@@ -1502,32 +1696,10 @@ class N_GET(DIMSEPrimitive):
 
         Parameters
         ----------
-        value : pydicom.uid.UID, bytes or str
-            The value for the Requested SOP Instance UID
+        pydicom.uid.UID, bytes or str
+            The value to use for the *Requested SOP Instance UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Requested SOP Instance UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Requested SOP Instance UID is an invalid UID")
-            raise ValueError("Requested SOP Instance UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Requested SOP Instance UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._requested_sop_instance_uid = value
+        self._RequestedSOPInstanceUID = value
 
 
 class N_SET(DIMSEPrimitive):
@@ -1617,8 +1789,8 @@ class N_SET(DIMSEPrimitive):
 
     @property
     def AffectedSOPInstanceUID(self):
-        """Return the *Affected SOP Instance UID*."""
-        return self._affected_sop_instance_uid
+        """Return the *Affected SOP Instance UID* as UID."""
+        return self._AffectedSOPInstanceUID
 
     @AffectedSOPInstanceUID.setter
     def AffectedSOPInstanceUID(self, value):
@@ -1627,56 +1799,46 @@ class N_SET(DIMSEPrimitive):
         Parameters
         ----------
         value : pydicom.uid.UID, bytes or str
-            The value for the Affected SOP Instance UID
+            The value to use for the *Affected SOP Class UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Affected SOP Instance UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Affected SOP Instance UID is an invalid UID")
-            raise ValueError("Affected SOP Instance UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Affected SOP Instance UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._affected_sop_instance_uid = value
+        self._AffectedSOPInstanceUID = value
 
     @property
     def AttributeList(self):
-        """Return the *Attribute List*."""
+        """Return the *Attribute List* as io.BytesIO."""
         return self._dataset_variant
 
     @AttributeList.setter
     def AttributeList(self, value):
-        """Set the *Attribute List*."""
+        """Set the *Attribute List*.
+
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Attribute List* parameter.
+        """
         self._dataset_variant = (value, 'AttributeList')
 
     @property
     def ModificationList(self):
-        """Return the *Modification List*."""
+        """Return the *Modification List* as io.BytesIO."""
         return self._dataset_variant
 
     @ModificationList.setter
     def ModificationList(self, value):
-        """Set the *Modification List*."""
+        """Set the *Modification List*.
+
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Modification List* parameter.
+        """
         self._dataset_variant = (value, 'ModificationList')
 
     @property
     def RequestedSOPClassUID(self):
-        """Return the *Requested SOP Class UID*."""
-        return self._requested_sop_class_uid
+        """Return the *Requested SOP Class UID* as UID."""
+        return self._RequestedSOPClassUID
 
     @RequestedSOPClassUID.setter
     def RequestedSOPClassUID(self, value):
@@ -1684,37 +1846,15 @@ class N_SET(DIMSEPrimitive):
 
         Parameters
         ----------
-        value : pydicom.uid.UID, bytes or str
-            The value for the Requested SOP Class UID
+        pydicom.uid.UID, bytes or str
+            The value to use for the *Requested SOP Class UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Requested SOP Class UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Requested SOP Class UID is an invalid UID")
-            raise ValueError("Requested SOP Class UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Requested SOP Class UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._requested_sop_class_uid = value
+        self._RequestedSOPClassUID = value
 
     @property
     def RequestedSOPInstanceUID(self):
-        """Return the *Requested SOP Instance UID*."""
-        return self._requested_sop_instance_uid
+        """Return the *Requested SOP Instance UID* as UID."""
+        return self._RequestedSOPInstanceUID
 
     @RequestedSOPInstanceUID.setter
     def RequestedSOPInstanceUID(self, value):
@@ -1722,32 +1862,10 @@ class N_SET(DIMSEPrimitive):
 
         Parameters
         ----------
-        value : pydicom.uid.UID, bytes or str
-            The value for the Requested SOP Instance UID
+        pydicom.uid.UID, bytes or str
+            The value to use for the *Requested SOP Instance UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Requested SOP Instance UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Requested SOP Instance UID is an invalid UID")
-            raise ValueError("Requested SOP Instance UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Requested SOP Instance UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._requested_sop_instance_uid = value
+        self._RequestedSOPInstanceUID = value
 
 
 class N_ACTION(DIMSEPrimitive):
@@ -1842,32 +1960,50 @@ class N_ACTION(DIMSEPrimitive):
 
     @property
     def ActionInformation(self):
-        """Return the *Action Information*."""
+        """Return the *Action Information* as io.BytesIO."""
         return self._dataset_variant
 
     @ActionInformation.setter
     def ActionInformation(self, value):
-        """Set the *Action Information*."""
+        """Set the *Action Information*.
+
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Action Information* parameter.
+        """
         self._dataset_variant = (value, 'ActionInformation')
 
     @property
     def ActionReply(self):
-        """Return the *Action Reply*."""
+        """Return the *Action Reply* as io.BytesIO."""
         return self._dataset_variant
 
     @ActionReply.setter
     def ActionReply(self, value):
-        """Set the *Action Reply List*."""
+        """Set the *Action Reply*.
+
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Action Reply* parameter.
+        """
         self._dataset_variant = (value, 'ActionReply')
 
     @property
     def ActionTypeID(self):
-        """Return the *Action Type ID*."""
+        """Return the *Action Type ID* as int."""
         return self._action_type_id
 
     @ActionTypeID.setter
     def ActionTypeID(self, value):
-        """Set the *Action Type ID*."""
+        """Set the *Action Type ID*.
+
+        Parameters
+        ----------
+        int
+            The value to use for the *Action Type ID* parameter.
+        """
         if isinstance(value, int) or value is None:
             self._action_type_id = value
         else:
@@ -1875,8 +2011,8 @@ class N_ACTION(DIMSEPrimitive):
 
     @property
     def AffectedSOPInstanceUID(self):
-        """Return the *Affected SOP Instance UID*."""
-        return self._affected_sop_instance_uid
+        """Return the *Affected SOP Instance UID* as UID."""
+        return self._AffectedSOPInstanceUID
 
     @AffectedSOPInstanceUID.setter
     def AffectedSOPInstanceUID(self, value):
@@ -1885,36 +2021,14 @@ class N_ACTION(DIMSEPrimitive):
         Parameters
         ----------
         value : pydicom.uid.UID, bytes or str
-            The value for the Affected SOP Instance UID
+            The value to use for the *Affected SOP Class UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Affected SOP Instance UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Affected SOP Instance UID is an invalid UID")
-            raise ValueError("Affected SOP Instance UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Affected SOP Instance UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._affected_sop_instance_uid = value
+        self._AffectedSOPInstanceUID = value
 
     @property
     def RequestedSOPClassUID(self):
-        """Return the *Requested SOP Class UID*."""
-        return self._requested_sop_class_uid
+        """Return the *Requested SOP Class UID* as UID."""
+        return self._RequestedSOPClassUID
 
     @RequestedSOPClassUID.setter
     def RequestedSOPClassUID(self, value):
@@ -1922,37 +2036,15 @@ class N_ACTION(DIMSEPrimitive):
 
         Parameters
         ----------
-        value : pydicom.uid.UID, bytes or str
-            The value for the Requested SOP Class UID
+        pydicom.uid.UID, bytes or str
+            The value to use for the *Requested SOP Class UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Requested SOP Class UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Requested SOP Class UID is an invalid UID")
-            raise ValueError("Requested SOP Class UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Requested SOP Class UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._requested_sop_class_uid = value
+        self._RequestedSOPClassUID = value
 
     @property
     def RequestedSOPInstanceUID(self):
-        """Return the *Requested SOP Instance UID*."""
-        return self._requested_sop_instance_uid
+        """Return the *Requested SOP Instance UID* as UID."""
+        return self._RequestedSOPInstanceUID
 
     @RequestedSOPInstanceUID.setter
     def RequestedSOPInstanceUID(self, value):
@@ -1960,32 +2052,10 @@ class N_ACTION(DIMSEPrimitive):
 
         Parameters
         ----------
-        value : pydicom.uid.UID, bytes or str
-            The value for the Requested SOP Instance UID
+        pydicom.uid.UID, bytes or str
+            The value to use for the *Requested SOP Instance UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Requested SOP Instance UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Requested SOP Instance UID is an invalid UID")
-            raise ValueError("Requested SOP Instance UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Requested SOP Instance UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._requested_sop_instance_uid = value
+        self._RequestedSOPInstanceUID = value
 
 
 class N_CREATE(DIMSEPrimitive):
@@ -2057,8 +2127,8 @@ class N_CREATE(DIMSEPrimitive):
 
     @property
     def AffectedSOPInstanceUID(self):
-        """Return the *Affected SOP Instance UID*."""
-        return self._affected_sop_instance_uid
+        """Return the *Affected SOP Instance UID* as UID."""
+        return self._AffectedSOPInstanceUID
 
     @AffectedSOPInstanceUID.setter
     def AffectedSOPInstanceUID(self, value):
@@ -2067,40 +2137,24 @@ class N_CREATE(DIMSEPrimitive):
         Parameters
         ----------
         value : pydicom.uid.UID, bytes or str
-            The value for the Affected SOP Instance UID
+            The value to use for the *Affected SOP Class UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Affected SOP Instance UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Affected SOP Instance UID is an invalid UID")
-            raise ValueError("Affected SOP Instance UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Affected SOP Instance UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._affected_sop_instance_uid = value
+        self._AffectedSOPInstanceUID = value
 
     @property
     def AttributeList(self):
-        """Return the *Attribute List*."""
+        """Return the *Attribute List* as io.BytesIO."""
         return self._dataset_variant
 
     @AttributeList.setter
     def AttributeList(self, value):
-        """Set the *Attribute List*."""
+        """Set the *Attribute List*.
+
+        Parameters
+        ----------
+        io.BytesIO
+            The value to use for the *Attribute List* parameter.
+        """
         self._dataset_variant = (value, 'AttributeList')
 
 
@@ -2178,8 +2232,8 @@ class N_DELETE(DIMSEPrimitive):
 
     @property
     def AffectedSOPInstanceUID(self):
-        """Return the *Affected SOP Instance UID*."""
-        return self._affected_sop_instance_uid
+        """Return the *Affected SOP Instance UID* as UID."""
+        return self._AffectedSOPInstanceUID
 
     @AffectedSOPInstanceUID.setter
     def AffectedSOPInstanceUID(self, value):
@@ -2188,36 +2242,14 @@ class N_DELETE(DIMSEPrimitive):
         Parameters
         ----------
         value : pydicom.uid.UID, bytes or str
-            The value for the Affected SOP Instance UID
+            The value to use for the *Affected SOP Class UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Affected SOP Instance UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Affected SOP Instance UID is an invalid UID")
-            raise ValueError("Affected SOP Instance UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Affected SOP Instance UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._affected_sop_instance_uid = value
+        self._AffectedSOPInstanceUID = value
 
     @property
     def RequestedSOPClassUID(self):
-        """Return the *Requested SOP Class UID*."""
-        return self._requested_sop_class_uid
+        """Return the *Requested SOP Class UID* as UID."""
+        return self._RequestedSOPClassUID
 
     @RequestedSOPClassUID.setter
     def RequestedSOPClassUID(self, value):
@@ -2225,37 +2257,15 @@ class N_DELETE(DIMSEPrimitive):
 
         Parameters
         ----------
-        value : pydicom.uid.UID, bytes or str
-            The value for the Requested SOP Class UID
+        pydicom.uid.UID, bytes or str
+            The value to use for the *Requested SOP Class UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Requested SOP Class UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Requested SOP Class UID is an invalid UID")
-            raise ValueError("Requested SOP Class UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Requested SOP Class UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._requested_sop_class_uid = value
+        self._RequestedSOPClassUID = value
 
     @property
     def RequestedSOPInstanceUID(self):
-        """Return the *Requested SOP Instance UID*."""
-        return self._requested_sop_instance_uid
+        """Return the *Requested SOP Instance UID* as UID."""
+        return self._RequestedSOPInstanceUID
 
     @RequestedSOPInstanceUID.setter
     def RequestedSOPInstanceUID(self, value):
@@ -2263,29 +2273,7 @@ class N_DELETE(DIMSEPrimitive):
 
         Parameters
         ----------
-        value : pydicom.uid.UID, bytes or str
-            The value for the Requested SOP Instance UID
+        pydicom.uid.UID, bytes or str
+            The value to use for the *Requested SOP Instance UID* parameter.
         """
-        if isinstance(value, UID):
-            pass
-        elif isinstance(value, str):
-            value = UID(value)
-        elif isinstance(value, bytes):
-            value = UID(value.decode('ascii'))
-        elif value is None:
-            pass
-        else:
-            raise TypeError("Requested SOP Instance UID must be a "
-                            "pydicom.uid.UID, str or bytes")
-
-        if value is not None and not validate_uid(value):
-            LOGGER.error("Requested SOP Instance UID is an invalid UID")
-            raise ValueError("Requested SOP Instance UID is an invalid UID")
-
-        if value and not value.is_valid:
-            LOGGER.warning(
-                "The Requested SOP Instance UID '{}' is non-conformant"
-                .format(value)
-            )
-
-        self._requested_sop_instance_uid = value
+        self._RequestedSOPInstanceUID = value


Test patch:
diff --git a/pynetdicom/tests/encoded_dimse_msg.py b/pynetdicom/tests/encoded_dimse_msg.py
index 472ae87b2..653af818b 100644
--- a/pynetdicom/tests/encoded_dimse_msg.py
+++ b/pynetdicom/tests/encoded_dimse_msg.py
@@ -199,10 +199,3 @@ c_get_rsp_cmd = b'\x03\x00\x00\x00\x00\x04\x00\x00\x00\x72' \
 c_get_rsp_ds = b'\x02\x08\x00\x52\x00\x08\x00\x00\x00\x50' \
                b'\x41\x54\x49\x45\x4e\x54\x20\x10\x00\x20' \
                b'\x00\x02\x00\x00\x00\x2a\x20'
-
-################################ N-EVENT-REPORT ################################
-##################################### N-GET ####################################
-##################################### N-SET ####################################
-################################### N-DELETE ###################################
-################################### N-ACTION ###################################
-################################### N-CREATE ###################################
diff --git a/pynetdicom/tests/encoded_dimse_n_msg.py b/pynetdicom/tests/encoded_dimse_n_msg.py
index 76562f46c..5ed873e3e 100644
--- a/pynetdicom/tests/encoded_dimse_n_msg.py
+++ b/pynetdicom/tests/encoded_dimse_n_msg.py
@@ -171,6 +171,22 @@ n_set_rq_cmd = (
     b'\x34\x38'
 )
 
+n_set_rq_cmd_empty = (
+    b'\x03'
+    # Command Group Length, length 4, value 46
+    b'\x00\x00\x00\x00\x04\x00\x00\x00\x2e\x00\x00\x00'
+    # Requested SOP Class UID, length 0
+    b'\x00\x00\x03\x00\x00\x00\x00\x00'
+    # Command Field   | length 2      | value 0x0120
+    b'\x00\x00\x00\x01\x02\x00\x00\x00\x20\x01'
+    # Message ID      | length 2      | value 7
+    b'\x00\x00\x10\x01\x02\x00\x00\x00\x07\x00'
+    # Command Data Set Type, length 2 | value 0x0101
+    b'\x00\x00\x00\x08\x02\x00\x00\x00\x01\x01'
+    # Requested SOP Instance UID, length 0
+    b'\x00\x00\x01\x10\x00\x00\x00\x00'
+)
+
 n_set_rq_ds = (
     # Message control header byte: dataset, last fragment
     b'\x02'
@@ -292,6 +308,23 @@ n_create_rq_cmd = (
     b'\x34\x38'
 )
 
+n_create_rq_cmd_empty = (
+    # Message control header byte: command set, last fragment
+    b'\x03'
+    # Command Group Length, length 4, value
+    b'\x00\x00\x00\x00\x04\x00\x00\x00\x36\x00\x00\x00'
+    # Affected SOP Class UID, length 8, value 1.2.3.4
+    b'\x00\x00\x02\x00\x08\x00\x00\x00\x31\x2e\x32\x2e\x33\x2e\x34\x00'
+    # Command Field   | length 2      | value 0x0140
+    b'\x00\x00\x00\x01\x02\x00\x00\x00\x40\x01'
+    # Message ID      | length 2      | value 7
+    b'\x00\x00\x10\x01\x02\x00\x00\x00\x07\x00'
+    # Command Data Set Type, length 2 | value 0x0101
+    b'\x00\x00\x00\x08\x02\x00\x00\x00\x01\x01'
+    # Affected SOP Instance UID, length 0
+    b'\x00\x00\x00\x10\x00\x00\x00\x00'
+)
+
 n_create_rq_ds = (
     # Message control header byte: dataset, last fragment
     b'\x02'
diff --git a/pynetdicom/tests/test_dimse_msg.py b/pynetdicom/tests/test_dimse_msg.py
index 38f6e0b09..be886d0af 100644
--- a/pynetdicom/tests/test_dimse_msg.py
+++ b/pynetdicom/tests/test_dimse_msg.py
@@ -34,7 +34,8 @@ from .encoded_dimse_n_msg import (
     n_delete_rq_cmd, n_delete_rsp_cmd,
     n_action_rq_cmd, n_action_rq_ds, n_action_rsp_cmd, n_action_rsp_ds,
     n_create_rq_cmd, n_create_rq_ds, n_create_rsp_cmd, n_create_rsp_ds,
-    n_set_rq_cmd, n_set_rq_ds, n_set_rsp_cmd, n_set_rsp_ds
+    n_set_rq_cmd, n_set_rq_ds, n_set_rsp_cmd, n_set_rsp_ds,
+    n_create_rq_cmd_empty, n_set_rq_cmd_empty
 )
 
 
@@ -620,6 +621,34 @@ class TestDIMSEMessage(object):
         assert primitive.MessageIDBeingRespondedTo == 5
         assert primitive.Status == 0xC201
 
+    def test_empty_uid_n_create(self):
+        """Test converting N_CREATE_RQ with empty UID value."""
+        msg = N_CREATE_RQ()
+        for data in [n_create_rq_cmd_empty]:
+            p_data = P_DATA()
+            p_data.presentation_data_value_list.append([0, data])
+            msg.decode_msg(p_data)
+
+        primitive = msg.message_to_primitive()
+        assert isinstance(primitive, N_CREATE)
+        assert primitive.AffectedSOPClassUID == UID('1.2.3.4')
+        assert primitive.MessageID == 7
+        assert primitive.AffectedSOPInstanceUID is None
+
+    def test_empty_uid_n_set(self):
+        """Test converting N_CREATE_RQ with empty UID value."""
+        msg = N_SET_RQ()
+        for data in [n_set_rq_cmd_empty]:
+            p_data = P_DATA()
+            p_data.presentation_data_value_list.append([0, data])
+            msg.decode_msg(p_data)
+
+        primitive = msg.message_to_primitive()
+        assert isinstance(primitive, N_SET)
+        assert primitive.RequestedSOPClassUID is None
+        assert primitive.MessageID == 7
+        assert primitive.RequestedSOPInstanceUID is None
+
 
 class TestThreadSafety(object):
     """Tests for the thread safety of DIMSEMessage classes."""
diff --git a/pynetdicom/tests/test_dimse_n.py b/pynetdicom/tests/test_dimse_n.py
index c953abc44..5df2980ff 100644
--- a/pynetdicom/tests/test_dimse_n.py
+++ b/pynetdicom/tests/test_dimse_n.py
@@ -1333,7 +1333,7 @@ class TestPrimitive_N_CREATE(object):
             primitive.Status = 19.4
 
     def test_conversion_rq(self):
-        """ Check conversion to a -RQ PDU produces the correct output """
+        """Check conversion to a -RQ PDU produces the correct output."""
         primitive = N_CREATE()
         primitive.MessageID = 7
         primitive.AffectedSOPClassUID = '1.2.840.10008.5.1.4.1.1.2'
@@ -1396,7 +1396,7 @@ class TestPrimitive_N_CREATE(object):
         primitive.AffectedSOPClassUID = '1.2'
         assert primitive.is_valid_request
 
-    def test_is_valid_resposne(self):
+    def test_is_valid_response(self):
         """Test N_CREATE.is_valid_response."""
         primitive = N_CREATE()
         assert not primitive.is_valid_response
diff --git a/pynetdicom/tests/test_service_qr.py b/pynetdicom/tests/test_service_qr.py
index 089513ca7..74ff88dec 100644
--- a/pynetdicom/tests/test_service_qr.py
+++ b/pynetdicom/tests/test_service_qr.py
@@ -833,6 +833,7 @@ class TestQRFindServiceClass(object):
         results = assoc.send_c_find(identifier, msg_id=11142, query_model='P')
         time.sleep(0.2)
         assoc.send_c_cancel(1, 3)
+        time.sleep(0.1)
         assoc.send_c_cancel(11142, 1)
 
         status, ds = next(results)
@@ -2612,6 +2613,7 @@ class TestQRGetServiceClass(object):
         results = assoc.send_c_get(identifier, msg_id=11142, query_model='P')
         time.sleep(0.3)
         assoc.send_c_cancel(1, 3)
+        time.sleep(0.1)
         assoc.send_c_cancel(11142, 1)
 
         status, ds = next(results)
@@ -4315,8 +4317,9 @@ class TestQRMoveServiceClass(object):
         results = assoc.send_c_move(identifier, move_aet=b'A',
                                     msg_id=11142, query_model='P')
         time.sleep(0.3)
-        assoc.send_c_cancel(11142, 1)
         assoc.send_c_cancel(1, 3)
+        time.sleep(0.1)
+        assoc.send_c_cancel(11142, 1)
 
         status, ds = next(results)
         assert status.Status == 0xFF00