private IList <TIdentifier> Query <TIdentifier, TFindScu>(TIdentifier queryCriteria, QueryRequest request, QueryResponse response)
            where TIdentifier : Identifier, new()
            where TFindScu : FindScuBase, new()
        {
            Platform.CheckForEmptyString(request.LocalApplicationEntity, "localAE");
            Platform.CheckForNullReference(request.RemoteApplicationEntity, "remoteAE");

            Platform.CheckForEmptyString(request.RemoteApplicationEntity.AETitle, "AETitle");

            Platform.CheckForNullReference(request.RemoteApplicationEntity.ScpParameters, "ScpParameters");
            Platform.CheckArgumentRange(request.RemoteApplicationEntity.ScpParameters.Port, 1, 65535, "Port");
            Platform.CheckForEmptyString(request.RemoteApplicationEntity.ScpParameters.HostName, "HostName");

            if (queryCriteria == null)
            {
                const string message = "The query identifier cannot be null.";
                Platform.Log(LogLevel.Error, message);
                throw new FaultException(message);
            }

            IList <DicomAttributeCollection> scuResults;

            using (var scu = new TFindScu())
            {
                if (request.MaxResults.HasValue)
                {
                    scu.MaxResults = request.MaxResults.Value;
                }

                DicomAttributeCollection criteria;

                var          oldCharacterSet = queryCriteria.SpecificCharacterSet;
                const string utf8            = "ISO_IR 192";
                //.NET strings are unicode, so the query criteria are unicode.
                queryCriteria.SpecificCharacterSet = utf8;

                try
                {
                    criteria = queryCriteria.ToDicomAttributeCollection();
                    criteria[DicomTags.InstanceAvailability] = null;
                    criteria[DicomTags.RetrieveAeTitle]      = null;
                }
                catch (DicomException e)
                {
                    var fault = new DataValidationFault
                    {
                        Description = "Failed to convert contract object to DicomAttributeCollection."
                    };
                    Platform.Log(LogLevel.Error, e, fault.Description);
                    throw new FaultException <DataValidationFault>(fault, fault.Description);
                }
                catch (Exception e)
                {
                    var fault = new DataValidationFault
                    {
                        Description = "Unexpected exception when converting contract object to DicomAttributeCollection."
                    };
                    Platform.Log(LogLevel.Error, e, fault.Description);
                    throw new FaultException <DataValidationFault>(fault, fault.Description);
                }
                finally
                {
                    queryCriteria.SpecificCharacterSet = oldCharacterSet;
                }

                try
                {
                    scuResults = scu.Find(request.LocalApplicationEntity, request.RemoteApplicationEntity.AETitle, request.RemoteApplicationEntity.ScpParameters.HostName, request.RemoteApplicationEntity.ScpParameters.Port, criteria);
                    scu.Join();

                    if (scu.Status == ScuOperationStatus.ConnectFailed)
                    {
                        String message = String.Format("Connection failed ({0})",
                                                       scu.FailureDescription ?? "no failure description provided");
                        var fault = new QueryFailedFault {
                            Description = message
                        };
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                    if (scu.Status == ScuOperationStatus.AssociationRejected)
                    {
                        String message = String.Format("Association rejected ({0})",
                                                       scu.FailureDescription ?? "no failure description provided");
                        var fault = new QueryFailedFault {
                            Description = message
                        };
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                    if (scu.Status == ScuOperationStatus.Failed)
                    {
                        String message = String.Format("The query operation failed ({0})",
                                                       scu.FailureDescription ?? "no failure description provided");
                        var fault = new QueryFailedFault {
                            Description = message
                        };
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                    if (scu.Status == ScuOperationStatus.TimeoutExpired)
                    {
                        String message = String.Format("The connection timeout expired ({0})",
                                                       scu.FailureDescription ?? "no failure description provided");
                        var fault = new QueryFailedFault {
                            Description = message
                        };
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                    if (scu.Status == ScuOperationStatus.NetworkError)
                    {
                        String message = String.Format("An unexpected network error has occurred.");
                        var    fault   = new QueryFailedFault {
                            Description = message
                        };
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                    if (scu.Status == ScuOperationStatus.UnexpectedMessage)
                    {
                        String message = String.Format("An unexpected message was received; aborted association.");
                        var    fault   = new QueryFailedFault {
                            Description = message
                        };
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }

                    response.MaxResultsReached = scu.Status == ScuOperationStatus.Canceled;

                    // Narrow down the results to the exact amount asked for
                    if (scu.MaxResults != -1 && scu.MaxResults < scuResults.Count)
                    {
                        response.MaxResultsReached = true;
                        scuResults = scuResults.Take(scu.MaxResults).ToList();
                    }
                }
                catch (FaultException)
                {
                    throw;
                }
                catch (Exception e)
                {
                    var fault = new QueryFailedFault
                    {
                        Description = String.Format("An unexpected error has occurred ({0})",
                                                    scu.FailureDescription ?? "no failure description provided")
                    };
                    Platform.Log(LogLevel.Error, e, fault.Description);
                    throw new FaultException <QueryFailedFault>(fault, fault.Description);
                }
            }

            var results = new List <TIdentifier>(scuResults.Count);

            foreach (DicomAttributeCollection result in scuResults)
            {
                var identifier = Identifier.FromDicomAttributeCollection <TIdentifier>(result);
                if (String.IsNullOrEmpty(identifier.RetrieveAeTitle) || identifier.RetrieveAeTitle == request.RemoteApplicationEntity.AETitle)
                {
                    identifier.RetrieveAE = request.RemoteApplicationEntity;
                }

                results.Add(identifier);
            }

            return(results);
        }
        private IList <TIdentifier> Query <TIdentifier, TFindScu>(TIdentifier queryCriteria)
            where TIdentifier : Identifier, new()
            where TFindScu : FindScuBase, new()
        {
            Platform.CheckForEmptyString(_localAE, "localAE");
            Platform.CheckForEmptyString(_remoteAE, "remoteAE");
            Platform.CheckForEmptyString(_remoteHost, "remoteHost");
            Platform.CheckArgumentRange(_remotePort, 1, 65535, "remotePort");

            if (queryCriteria == null)
            {
                string message = "The query identifier cannot be null.";
                Platform.Log(LogLevel.Error, message);
                throw new FaultException(message);
            }

            IList <DicomAttributeCollection> scuResults;

            using (TFindScu scu = new TFindScu())
            {
                DicomAttributeCollection criteria;

                try
                {
                    criteria = queryCriteria.ToDicomAttributeCollection();
                }
                catch (DicomException e)
                {
                    DataValidationFault fault = new DataValidationFault();
                    fault.Description = "Failed to convert contract object to DicomAttributeCollection.";
                    Platform.Log(LogLevel.Error, e, fault.Description);
                    throw new FaultException <DataValidationFault>(fault, fault.Description);
                }
                catch (Exception e)
                {
                    DataValidationFault fault = new DataValidationFault();
                    fault.Description = "Unexpected exception when converting contract object to DicomAttributeCollection.";
                    Platform.Log(LogLevel.Error, e, fault.Description);
                    throw new FaultException <DataValidationFault>(fault, fault.Description);
                }

                try
                {
                    scuResults = scu.Find(_localAE, _remoteAE, _remoteHost, _remotePort, criteria);
                    scu.Join();

                    if (scu.Status == ScuOperationStatus.Canceled)
                    {
                        String message = String.Format("The remote server cancelled the query ({0})",
                                                       scu.FailureDescription ?? "no failure description provided");
                        QueryFailedFault fault = new QueryFailedFault();
                        fault.Description = message;
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                    if (scu.Status == ScuOperationStatus.ConnectFailed)
                    {
                        String message = String.Format("Connection failed ({0})",
                                                       scu.FailureDescription ?? "no failure description provided");
                        QueryFailedFault fault = new QueryFailedFault();
                        fault.Description = message;
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                    if (scu.Status == ScuOperationStatus.AssociationRejected)
                    {
                        String message = String.Format("Association rejected ({0})",
                                                       scu.FailureDescription ?? "no failure description provided");
                        QueryFailedFault fault = new QueryFailedFault();
                        fault.Description = message;
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                    if (scu.Status == ScuOperationStatus.Failed)
                    {
                        String message = String.Format("The query operation failed ({0})",
                                                       scu.FailureDescription ?? "no failure description provided");
                        QueryFailedFault fault = new QueryFailedFault();
                        fault.Description = message;
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                    if (scu.Status == ScuOperationStatus.TimeoutExpired)
                    {
                        String message = String.Format("The connection timeout expired ({0})",
                                                       scu.FailureDescription ?? "no failure description provided");
                        QueryFailedFault fault = new QueryFailedFault();
                        fault.Description = message;
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                    if (scu.Status == ScuOperationStatus.NetworkError)
                    {
                        String           message = String.Format("An unexpected network error has occurred.");
                        QueryFailedFault fault   = new QueryFailedFault();
                        fault.Description = message;
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                    if (scu.Status == ScuOperationStatus.UnexpectedMessage)
                    {
                        String           message = String.Format("An unexpected message was received; aborted association.");
                        QueryFailedFault fault   = new QueryFailedFault();
                        fault.Description = message;
                        throw new FaultException <QueryFailedFault>(fault, fault.Description);
                    }
                }
                catch (FaultException)
                {
                    throw;
                }
                catch (Exception e)
                {
                    QueryFailedFault fault = new QueryFailedFault();
                    fault.Description = String.Format("An unexpected error has occurred ({0})",
                                                      scu.FailureDescription ?? "no failure description provided");
                    Platform.Log(LogLevel.Error, e, fault.Description);
                    throw new FaultException <QueryFailedFault>(fault, fault.Description);
                }
            }

            List <TIdentifier> results = new List <TIdentifier>();

            foreach (DicomAttributeCollection result in scuResults)
            {
                TIdentifier identifier = Identifier.FromDicomAttributeCollection <TIdentifier>(result);
                if (String.IsNullOrEmpty(identifier.RetrieveAeTitle))
                {
                    identifier.RetrieveAeTitle = _remoteAE;
                }

                results.Add(identifier);
            }

            return(results);
        }