Introduction
Custom Medical Storage Data Access Layer
A Custom Medical Storage Data Access Layer must contain the following classes:
Requirement
|
My.Medical.Storage.DataAccessLayer Implementation
|
Data access agent that implements IStorageDataAccessAgent
|
class MyStorageSqlDbDataAccessAgent
|
Data access configuration view that implements IDataAccessConfigurationView. This helper class is used to create the data access agent.
|
class MyStorageDataAccessConfigurationView
|
One class for each table that extracts DICOM data from System.Data.DataRow
|
class MyPatientInfo : IPatientInfo
class MyStudyInfo : IStudyInfo
class MySeriesInfo : ISeriesInfo
class MyInstanceInfo : IInstanceInfo
|
A strongly typed class that represents the custom database (derives from System.Data.DataSet )
|
class MyDataSet : System.Data.DataSet
|
One class for each table that is used with the MatchingParameterCollection to generate the WHERE statement of the database query
|
class MyPatient : CatalogEntity
class MyStudy : CatalogEntity
class MySeries : CatalogEntity
class MyInstance : CatalogEntity
|
The medical storage data access layer used in the tutorial also includes the following helper classes, which are not strictly required but simplify implementation:
Helper Class
|
My.Medical.Storage.DataAccessLayer Implementation
|
Extracts the patient, study, series, and instance information from a DicomDataSet object, and add/updates the System.Data.DataSet tables (patient, study, series, instance) accordingly
|
class MyDicomDataSetConvertor
|
SQL query statements and statement fragments used to build SQL query statements
|
class MyConstants
class MySqlStatments
|
The sample custom medical storage data access layer is called My.Medical.Storage.DataAccessLayer. It is included in the LEAD installation as a sample project. It contains all of the classes in the two tables.
Open the project file in Visual Studio, and you will see the files in the project:
The following table describes the contents of each file
Folder
|
FileName
|
Contents
|
Configuration
|
|
Classes to extract DICOM data from a System.Data.DataRow
|
|
MyPatientInfo.cs
|
class MyPatientInfo : IPatientInfo
|
|
MyStudyInfo.cs
|
class MyStudyInfo : IStudyInfo
|
|
MySeriesInfo.cs
|
class MySeriesInfo : ISeriesInfo
|
|
MyInstanceInfo.cs
|
class MyInstanceInfo : IInstanceInfo
|
DataAccessLogic\BusinessEntity
|
|
A strongly typed class that represents an in-memory representation of your custom database
|
|
MyDataSet.xsd
|
Schema for custom database
|
|
MyDataSet.cs
|
class MyDataSet
|
DataAccessLogic\ComponentFactory
|
|
Helper class used to create the data access agent
|
|
MyDataAccessConfigurationView.cs
|
class MyStorageDataAccessConfigurationView
|
DataAccessLogic\DataAccessAgent\Database\SqlServer
|
|
Implementation of IStorageDataAccessAgent that uses SQL-specific methods
|
|
MyCommandText.cs
|
class MyStorageSqlDbDataAccessAgent
|
|
MyConstants.cs
|
class MyConstants
class MySqlStatments
|
|
MyStorageSqlDataAccessAgent.cs
|
class MyStorageSqlDbDataAccessAgent
|
DataAccessLogic\DataAccessAgent
|
|
Implementation of IStorageDataAccessAgent , containing methods that are not SQL-specific
|
|
MyStorageDbDataAccessAgent.cs
|
class MyStorageSqlDbDataAccessAgent
|
DataAccessLogic\DicomDataSetConvertor
|
|
|
|
MyDicomDataSetConvertor.cs
|
class MyDicomDataSetConvertor
|
Entities
|
|
classes used with the MatchingParameterCollection to generate the WHERE statement of the database query
|
|
MyPatientBase.cs
|
class MyPatient : CatalogEntity
|
|
MyStudyBase.cs
|
class MyStudy< : CatalogEntity
|
|
MySeriesBase.cs
|
class MySeriesInfo : ISeriesInfo
|
|
MyInstanceBase.cs
|
class MyInstanceInfo : IInstanceInfo
|
Utilities
|
|
Utilities class containing extension methods
|
|
Utils.cs
|
class MyUtils
|
These classes are described in detail below, and organized by namespace
namespace My.Medical.Storage.DataAccessLayer
- class MyPatientInfo : IPatientInfo
- class MyStudyInfo : IStudyInfo
- class MySeriesInfo : ISeriesInfo
-
class MyInstanceInfo : IInstanceInfo
- Implements an interface to extract DICOM data from a System.Data.DataRow
-
class MyStorageDataAccessConfigurationView
- Helper class used to create the data access agent
- Internally stores the type that implements IStorageDataAccessAgent (MyStorageSqlDbDataAccessAgent), so it can be created dynamically
- Associates a DataAccessLayer with a DataAccessAgent and a configuration file (GlobalPacs.config)
- Defines the DataAccessLayer section name (used in GlobalPacs.config)
- GlobalPacs.config in turn associates the DataAccessLayer section name with a connection string
-
class MyStorageSqlDbDataAccessAgent
- Provides methods to create SQL queries to access (insert, update, delete) the table entries in the database
- The methods override the default behavior of the StorageSqlDbDataAccessAgent.
-
Query Commands
- protected override void PreparePatientsQueryCommand
- protected override void PrepareStudiesQueryCommand
- protected override void PrepareSeriesQueryCommand
- protected override void PrepareInstanceQueryCommand
- Creates a command to query the database based on a level (i.e PrepareSeriesQueryCommand creates a command for a series level database query)
- protected override void PrepareInstancePageQueryCommand
- Prepares an Instance level command when using pagination. Pagination returns results in one or more pages of data, where the user specifies the page size. The pagination feature is not implemented in this sample data access agent.
-
Count Commands
- protected override void PreparePatientsQueryCountCommand
- protected override void PrepareStudiesQueryCountCommand
- protected override void PrepareSeriesQueryCountCommand
- protected override void PrepareInstanceQueryCountCommand
- Creates a command that returns a count of rows matching the search criteria specified in the matchingParamsCollection
-
Delete Commands
- protected override void PrepareDeletePatientsCommand
- protected override void PrepareDeleteStudiesCommand
- protected override void PrepareDeleteSeriesCommand
- protected override void PrepareDeleteInstanceCommand
- Creates a command to delete rows of a System.Data.DataTable based on the search criteria specified in the matchingParamsCollection
- protected override void PrepareDeletePatientsNoChildStudiesCommand
- protected override void PrepareDeleteStudiesNoChildSeriesCommand
- protected override void PrepareDeleteSeriesNoChildInstancesCommand
- Creates a command to delete rows from a table that has no corresponding rows in a child table. For example, the PrepareDeletePatientsNoChildStudiesCommand command would be invoked if a MyPatient table entry had no corresponding entry in the MyStudy table.
- public override int DeletePatient
- public override int DeleteStudy
- public override int DeleteSeries
- public override int DeleteInstance
- High-level commands that deletes rows from a database table based on the search criteria specified in the matchingParamsCollection
- Uses the PrepareDeleteXxxxCommand() overrides
- These commands also delete the corresponding rows in the tables above it in the hierarchy
- For example, DeleteStudy will delete one or more rows from the MyStudy table. If the corresponding MyPatientTable patient rows contain no more studies, then these are also deleted.
-
Exists Commands
- protected override void PrepareIsPatientExistsCommand
- protected override void PrepareIsStudiesExistsCommand
- protected override void PrepareIsSeriesExistsCommand
- protected override void PrepareIsInstanceExistsCommand
- Creates a command to test for existence based on unique ID (PatientID, StudyInstanceUID, SeriesInstanceUID, or SOPInstanceUID)
-
Miscellaneous Commands
- protected override string[] GetCompositeInstanceQueryDataAdapterTables
- Returns a list containing the all tables names in your database
- protected override DataSet CreateCompositeInstanceDataSet
-
Creates a DataSet representing the tables of your database. This is actually a strongly typed, but it is returned as the base class DataSet. The default implementation returns CompositeInstanceDataSet. For the tutorial, you will return the strongly typed MyDataSet.
- MyPatient
- MyStudy
- MySeries
- MyInstance
-
public virtual void UpdateCompositeInstance
- Updates the necessary table entries (MyPatient, MyStudy, MySeries, MyInstance) in the database, based on the input parameter myDataSet.
- myDataSet is a DataSet that contains all the necessary additions, updates, and deletions that need to be applied
- If there is an exception during the process, all updates are backed out.
-
public override void StoreDicom
- Creates the necessary table entries (MyPatient, MyStudy, MySeries, MyInstance) for a Leadtools.Dicom.DicomDataSet
- Table rows are overwritten based on the parameters
- updateExistentPatient
- updateExistentStudy
- updateExistentSeries
- updateExistentInstances
namespace My.Medical.Storage.DataAccessLayer.DataAccessLogic.BusinessEntity
-
class MyDataSet
- A strongly typed class that represents an in-memory representation of your custom database
- Has members that allow read/write access to database fields
- Maintains list of foreign-key relationships between database tables
- This class can be generated using Visual Studio. For instructions, see Creating the Strongly Typed DataSet Class and the XML Schema File.
namespace My.Medical.Storage.DataAccessLayer.DataAccessLogic.DataAccessAgent.Database.SqlServer
- class MyConstants
-
class MySqlStatments
- SQL query statements and statement fragments used by MyStorageSqlDbDataAccessAgent class to query/update/and delete items from the database
namespace My.Medical.Storage.DataAccessLayer.DataAccessLogic.DicomDataSetConvertor
-
class MyDicomDataSetConvertor
- public void FillADONetDataSet
- Called by MyStorageSqlDbDataAccessAgent.StoreDicom
- Extracts the patient, study, series, and instance information from a DicomDataSet object, and add/updates the DataSet tables (patient, study, series, instance) accordingly.
- The update parameters (updateExistentPatient, updateExistentStudy, updateExistentSeries, updateExistentInstances) affect if corresponding existing DataSet rows remain unchanged, or are overwritten.
- For example, if updateExistentPatient is true and there is already a row the DataSet ‘MyPatient’ table with the same Patient ID, then the patient row is overwritten with new patient data.
- public bool AutoTruncate
- If true, data from the DicomDataSet is truncated if necessary, so that if fits in the DataTable column maximum length.
- private void FillPatientData
- Adds a new row to the DataSet Patient table (MyPatientTable) if the DicomDataSet PatientID does not already exist
- private void FillStudiesData
- Adds a new row to the DataSet Study Table (MyStudyTable) if the DicomDataSet StudyInstanceUID does not already exist
- private void FillSeriesData
- Adds a new row to the DataSet Study Table (MySeriesTable) if the DicomDataSet SeriesInstanceUID does not already exist
- private void FillInstancetData
- Adds a new row to the DataSet Study Table (MyInstanceTable) if the DicomDataSet SOPInstanceUID does not already exist
- private void FillPatientInformation
- If the ‘update’ parameter is true, replaces all the DataSet Patient table information with the corresponding information from the DicomDataSet.
-
This method needs to change based on your schema. For the tutorial schema, we update all fields in the patient table
- PatientName
- PatientBirthday
- PatientSex
- PatientComments
- private void FillStudyInformation
- If the ‘update’ parameter is true, replaces all the DataSet Study table information with the corresponding information from the DicomDataSet.
-
This method needs to change based on your schema. For the tutorial schema, we update all fields in the Study table
- StudyDate
- AccessionNumber
- StudyDescription
- StudyReferringPhysiciansName
- private void FillSeriesInformation
- If the ‘update’ parameter is true, replaces all the DataSet Series table information with the corresponding information from the DicomDataSet.
-
This method needs to change based on your schema. For the tutorial schema, we update all fields in the Series table
- SeriesNumber
- SeriesDate
- SeriesDescription
- Modality
- BodyPartExamined
-
private void FillInstanceInformation
- If the ‘update’ parameter is true, replaces all the DataSet Instance table information with the corresponding information from the DicomDataSet.
- This method needs to change based on your schema. For the tutorial schema, we update all fields in the Series table
- InstanceNumber
- SOPClassUID
- Rows
- Columns
- BitsAllocated
- ImageLastStoreDate - Updated to current date.
- ImageFilename - Full path to location where DicomDataSet is stored
namespace My.Medical.Storage.DataAccessLayer.Entities
- public class MyPatient
- public class MyStudy
- public class MySeries
-
public class MyInstance
- These classes are used with the MatchingParameterCollection to generate the WHERE statement of the database query.
- A MatchingParameterCollection contains one more MatchingParameterList
- A MatchingParameterList contains one or more ICatalogEntity
- The MyPatient, MyStudy, MySeries, and MyInstance classes each derive from CatalogEntity (which implements the ICatalogEntity interface).
- MyPatient contains a property for everything that can be queried in the MyPatientTable.
- Each property is decorated with the [EntityElementAttribute] attribute.
- For example, the MyPatientTable contains the following columns, any or all of which can be part of a query
- PatientId
- PatientIdentification
- PatientName
- PatientBirthday
- PatientSex
- PatientComments
- Any of the overrides in MyStorageSqlDbDataAccessAgent can be called with a MatchingParameterCollection
- Example: Prepare a System.Data.IDbCommand that generates a query or all male patients named ‘Smith’
// This example builds an IDbCommand
// for querying the MyPatient table
// The generated WHERE clause contains PatientName and PatientSex
public void MyExample()
{
MatchingParameterCollection matchingParamCollection = new MatchingParameterCollection();
MatchingParameterList matchingParamList = new MatchingParameterList();
MyPatient myPatient = new MyPatient();
myPatient.PatientName = "Smith";
myPatient.PatientSex = "M";
matchingParamList.Add(myPatient);
matchingParamCollection.Add(matchingParamList);
IDbCommand command = new SqlCommand();
// This reads the storage catalog
// Before you run this, make sure and add the following to your application configuration file
//<configSections>
// <section name="xmlStorageCatalogSettings" type="Leadtools.Medical.Storage.DataAccessLayer.XmlStorageCatalogSettings, Leadtools.Medical.Storage.DataAccessLayer" />
//</configSections>
StorageCatalog myCatalog = new StorageCatalog();
Collection<CatalogElement[]> catalogElements = CatalogDescriptor.GetElementsCollection(matchingParamCollection, myCatalog , true);
PreparePatientsQueryCommand(command, catalogElements);
// The 'WHERE' clause of command.CommandText is the following:
// WHERE ( ( MyPatientTable.PatientName LIKE 'Smith' ) AND ( MyPatientTable.PatientSex LIKE 'M' ) )
Console.WriteLine(command.CommandText);
}