예제 #1
0
 public CodeColumnExpressionCollection(ColumnSchema[] columns)
 {
     foreach (ColumnSchema columnSchema in columns)
     {
         this.Add(new CodeArgumentReferenceExpression(CommonConversion.ToCamelCase(columnSchema.Name)));
     }
 }
        /// <summary>
        /// Creates an object that translates the parameters used for external methods to parameters for internal methods.
        /// </summary>
        public DestroyParameterMatrix(TableSchema tableSchema)
        {
            // Initialize the object.
            this.ExternalParameterItems = new SortedList <string, ExternalParameterItem>();

            // Every set of update parameters requires a unique key to identify the record that is to be updated.
            UniqueConstraintParameterItem primaryKeyParameterItem = new UniqueConstraintParameterItem();

            primaryKeyParameterItem.ActualDataType   = typeof(System.Object[]);
            primaryKeyParameterItem.DeclaredDataType = typeof(System.Object[]);
            primaryKeyParameterItem.Description      = string.Format("The required key for the {0} table.", tableSchema.Name);
            primaryKeyParameterItem.FieldDirection   = FieldDirection.In;
            primaryKeyParameterItem.Name             = string.Format("{0}Key", CommonConversion.ToCamelCase(tableSchema.Name));
            this.ExternalParameterItems.Add(primaryKeyParameterItem.Name, primaryKeyParameterItem);

            // This will create an interface for the 'Destroy' method.
            foreach (KeyValuePair <string, ColumnSchema> columnPair in tableSchema.Columns)
            {
                // This column is turned into a simple parameter.
                ColumnSchema columnSchema = columnPair.Value;

                // The row version is required for the optimistic concurrency checking that is part of all update operations.
                if (columnSchema.IsRowVersion)
                {
                    SimpleParameterItem simpleParameterItem = new SimpleParameterItem();
                    simpleParameterItem.ActualDataType   = columnSchema.DataType;
                    simpleParameterItem.ColumnSchema     = columnSchema;
                    simpleParameterItem.DeclaredDataType = columnSchema.DataType;
                    simpleParameterItem.Description      = string.Format("The required value for the {0} column.", CommonConversion.ToCamelCase(columnSchema.Name));
                    simpleParameterItem.FieldDirection   = FieldDirection.In;
                    simpleParameterItem.Name             = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.ExternalParameterItems.Add(simpleParameterItem.Name, simpleParameterItem);
                }
            }
        }
예제 #3
0
 /// <summary>
 /// Represents an expression that creates an object array using an array of ColumnSchemas.
 /// </summary>
 /// <param name="columns">The columns used to create the array expression.</param>
 public CodeKeyCreateExpression(ColumnSchema[] columns)
 {
     this.CreateType = new CodeGlobalTypeReference(typeof(System.Object));
     foreach (ColumnSchema columnSchema in columns)
     {
         this.Initializers.Add(new CodeArgumentReferenceExpression(CommonConversion.ToCamelCase(columnSchema.Name)));
     }
 }
예제 #4
0
        /// <summary>
        /// Creates an object that translates the parameters used for external methods to parameters for internal methods.
        /// </summary>
        public UpdateParameterMatrix(TableSchema tableSchema)
        {
            // Initialize the object.
            this.ExternalParameterItems = new SortedList <string, ExternalParameterItem>();

            // Every set of update parameters requires a unique key to identify the record that is to be updated.
            UniqueConstraintParameterItem primaryKeyParameterItem = new UniqueConstraintParameterItem();

            primaryKeyParameterItem.ActualDataType   = typeof(System.Object[]);
            primaryKeyParameterItem.DeclaredDataType = typeof(System.Object[]);
            primaryKeyParameterItem.Description      = string.Format("The required key for the {0} table.", tableSchema.Name);
            primaryKeyParameterItem.FieldDirection   = FieldDirection.In;
            primaryKeyParameterItem.Name             = string.Format("{0}Key", CommonConversion.ToCamelCase(tableSchema.Name));
            this.ExternalParameterItems.Add(primaryKeyParameterItem.Name, primaryKeyParameterItem);

            // This will create an interface for the 'Update' method.
            foreach (KeyValuePair <string, ColumnSchema> columnPair in tableSchema.Columns)
            {
                // This column is turned into a simple parameter.
                ColumnSchema columnSchema = columnPair.Value;

                // If a column requires special processing, it is not handled with the rest of the parameters.
                bool isOrdinaryColumn = true;

                // The row version is required for the optimistic concurrency checking that is part of all update operations.
                if (columnSchema.IsRowVersion)
                {
                    isOrdinaryColumn = false;
                    SimpleParameterItem simpleParameterItem = new SimpleParameterItem();
                    simpleParameterItem.ActualDataType   = columnSchema.DataType;
                    simpleParameterItem.ColumnSchema     = columnSchema;
                    simpleParameterItem.DeclaredDataType = columnSchema.DataType;
                    simpleParameterItem.Description      = string.Format("The required value for the {0} column.", CommonConversion.ToCamelCase(columnSchema.Name));
                    simpleParameterItem.FieldDirection   = FieldDirection.In;
                    simpleParameterItem.Name             = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.ExternalParameterItems.Add(simpleParameterItem.Name, simpleParameterItem);
                }

                // Ordinary parameters are passed from the external caller to the internal methods without modification or
                // interpretation.
                if (isOrdinaryColumn)
                {
                    SimpleParameterItem simpleParameterItem = new SimpleParameterItem();
                    simpleParameterItem.ActualDataType   = columnSchema.DataType;
                    simpleParameterItem.ColumnSchema     = columnSchema;
                    simpleParameterItem.DeclaredDataType = typeof(System.Object);
                    simpleParameterItem.Description      = string.Format("The optional value for the {0} column.", CommonConversion.ToCamelCase(columnSchema.Name));
                    simpleParameterItem.FieldDirection   = FieldDirection.In;
                    simpleParameterItem.Name             = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.ExternalParameterItems.Add(simpleParameterItem.Name, simpleParameterItem);
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Creates an object that translates the parameters used for external methods to parameters for internal methods.
        /// </summary>
        public CreateParameterMatrix(TableSchema tableSchema)
        {
            // Initialize the object.
            this.ExternalParameterItems = new SortedList <string, ExternalParameterItem>();

            // This will create an interface for the 'Create' method.
            foreach (KeyValuePair <string, ColumnSchema> columnPair in tableSchema.Columns)
            {
                // This column is turned into a simple parameter.
                ColumnSchema columnSchema = columnPair.Value;

                // If a column requires special processing, it is not handled with the rest of the parameters.
                bool isOrdinaryColumn = true;

                // The row version is not used in the set of Create parameters.
                if (columnSchema.IsRowVersion)
                {
                    isOrdinaryColumn = false;
                }

                // AutoIncremented columns can only be specified as output parameters.
                if (columnSchema.IsAutoIncrement)
                {
                    isOrdinaryColumn = false;
                    SimpleParameterItem simpleParameterItem = new SimpleParameterItem();
                    simpleParameterItem.ActualDataType   = columnSchema.DataType;
                    simpleParameterItem.ColumnSchema     = columnSchema;
                    simpleParameterItem.DeclaredDataType = columnSchema.DataType;
                    simpleParameterItem.Description      = string.Format("The generated value for the {0} column.", columnSchema.Name);
                    simpleParameterItem.FieldDirection   = FieldDirection.Out;
                    simpleParameterItem.Name             = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.ExternalParameterItems.Add(simpleParameterItem.Name, simpleParameterItem);
                }

                // The only complication for ordinary parameters is whether the data type can accept a default or not.
                if (isOrdinaryColumn)
                {
                    SimpleParameterItem simpleParameterItem = new SimpleParameterItem();
                    bool isOptional = columnSchema.IsNullable || columnSchema.DefaultValue != DBNull.Value;
                    simpleParameterItem.ActualDataType   = columnSchema.DataType;
                    simpleParameterItem.ColumnSchema     = columnSchema;
                    simpleParameterItem.DeclaredDataType = isOptional ? typeof(System.Object) : columnSchema.DataType;
                    simpleParameterItem.Description      = string.Format("The {0} value for the {1} column.", isOptional ? "optional" : "required", columnSchema.Name);
                    simpleParameterItem.FieldDirection   = FieldDirection.In;
                    simpleParameterItem.Name             = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.ExternalParameterItems.Add(simpleParameterItem.Name, simpleParameterItem);
                }
            }
        }
        /// <summary>
        /// Creates an array of values from a unique constraint that can be used for finding records.
        /// </summary>
        /// <param name="uniqueConstraintSchema">A description of a unique constraint.</param>
        /// <returns>An array of expressions that can be used as a key for finding records in a table.</returns>
        public CodeExpression[] CreateKey(UniqueConstraintSchema uniqueConstraintSchema)
        {
            // This will cycle through all the foreign and simple parameters looking for any columns that match up to the child
            // columns of the constraint.  When found, they are placed in the array in the proper order to match up against the
            // given unique constraint.
            List <CodeExpression> keys = new List <CodeExpression>();

            foreach (ColumnSchema uniqueColumn in uniqueConstraintSchema.Columns)
            {
                foreach (KeyValuePair <string, ExternalParameterItem> parameterPair in this.ExternalParameterItems)
                {
                    // This correlates the unique constraint columns with the variables that have been created in the method to
                    // hold the key values from the foreign tables.  These variables exist outside of the conditional logic that
                    // finds the parent row, so if the parent key is null, these values will also be null.  However, since they're
                    // still declared, they can be used to construct keys, which is useful when they're optional values.
                    if (parameterPair.Value is ForeignKeyConstraintParameterItem)
                    {
                        ForeignKeyConstraintParameterItem foreignKeyConstraintParameterItem = parameterPair.Value as ForeignKeyConstraintParameterItem;
                        ForeignKeyConstraintSchema        foreignKeyConstraintSchema        = foreignKeyConstraintParameterItem.ForeignKeyConstraintSchema;
                        for (int columnIndex = 0; columnIndex < foreignKeyConstraintSchema.Columns.Length; columnIndex++)
                        {
                            if (uniqueColumn == foreignKeyConstraintSchema.Columns[columnIndex])
                            {
                                foreach (ForeignKeyVariableItem foreignKeyVariableItem in foreignKeyConstraintParameterItem.ForeignKeyVariables)
                                {
                                    if (foreignKeyConstraintSchema.Columns[columnIndex] == foreignKeyVariableItem.ColumnSchema)
                                    {
                                        keys.Add(foreignKeyVariableItem.Expression);
                                    }
                                }
                            }
                        }
                    }

                    // This will match the columns described in the simple parameters to the columns in the unique constraint.
                    if (parameterPair.Value is SimpleParameterItem)
                    {
                        SimpleParameterItem simpleParameterItem = parameterPair.Value as SimpleParameterItem;
                        if (uniqueColumn == simpleParameterItem.ColumnSchema)
                        {
                            keys.Add(new CodeVariableReferenceExpression(CommonConversion.ToCamelCase(simpleParameterItem.ColumnSchema.Name)));
                        }
                    }
                }
            }

            // This array can be used as a key to find the record in a table.
            return(keys.ToArray());
        }
 /// <summary>
 /// Generates a delegate for filtering records from the stream of data sent to the client.
 /// </summary>
 /// <param name="dataSetNamespace">The CodeDOM namespace FluidTrade.Core.FilterContextDelegate contains this strongly typed DataSet.</param>
 public FilterContextDelegate(DataModelSchema dataModelSchema)
 {
     //	/// <summary>
     //	/// A general purpose delegate to find a context for filtering results that are returned to the client.
     //	/// </summary>
     //	/// <param name="dataModelTransaction"></param>
     //	/// <returns>An object that can be used, often a user identifier, to determine whether a given row can be returned to the client.</returns>
     //	public delegate Object FilterContextDelegate(DataModelTransaction dataModelTransaction);
     this.Comments.Add(new CodeCommentStatement("<summary>", true));
     this.Comments.Add(new CodeCommentStatement("Handles filters that remove data from the stream returned to the client.", true));
     this.Comments.Add(new CodeCommentStatement("</summary>", true));
     this.Comments.Add(new CodeCommentStatement("<param name=\"dataModelTransaction\">The current transaction.</param>", true));
     this.Comments.Add(new CodeCommentStatement("<returns>An object that can be used, often a user identifier, to determine whether a given row can be returned to the client.</returns>", true));
     this.ReturnType = new CodeGlobalTypeReference(typeof(Object));
     this.Name       = "FilterContextDelegate";
     this.Parameters.Add(
         new CodeParameterDeclarationExpression(
             new CodeTypeReference(String.Format("{0}Transaction", dataModelSchema.Name)),
             String.Format("{0}Transaction", CommonConversion.ToCamelCase(dataModelSchema.Name))));
 }
        /// <summary>
        /// Creates an object that translates the parameters used for external methods to parameters for internal methods.
        /// </summary>
        public UpdateExParameterMatrix(TableSchema tableSchema)
        {
            // Initialize the object.
            this.ExternalParameterItems = new SortedList <string, ExternalParameterItem>();
            this.InternalParameters     = new SortedList <string, InternalParameterItem>();

            // Place all the columns of the table in a list that will drive the creation of parameters.  Those that columns that
            // are native to this table will be left in the list.  Those columns that are part of a foreign constraint will be
            // replace in the final argument list with internal values that can be used to look up the actual value.
            List <ColumnSchema> columnList = new List <ColumnSchema>();

            foreach (KeyValuePair <string, ColumnSchema> keyValuePair in tableSchema.Columns)
            {
                columnList.Add(keyValuePair.Value);
            }

            // Every set of parameters to the 'Update' method from an external interface requires a unique identifier in order to
            // find the record and update it.  This unique identifier can use any of the UniqueConstraints on the table in order to
            // find the record.  This creates a description of that parameter for the external method.
            UniqueConstraintParameterItem uniqueConstraintParameterItem = new UniqueConstraintParameterItem();

            uniqueConstraintParameterItem.ActualDataType = typeof(System.Object[]);
            uniqueConstraintParameterItem.CodeVariableReferenceExpression = new CodeRandomVariableReferenceExpression();
            uniqueConstraintParameterItem.DeclaredDataType = typeof(System.Object[]);
            uniqueConstraintParameterItem.Description      = string.Format("A required unique key for the {0} record.", tableSchema.Name);
            uniqueConstraintParameterItem.FieldDirection   = FieldDirection.In;
            uniqueConstraintParameterItem.Name             = string.Format("{0}Key", CommonConversion.ToCamelCase(tableSchema.Name));
            this.ExternalParameterItems.Add(uniqueConstraintParameterItem.Name, uniqueConstraintParameterItem);

            // Every set of parameters to the 'Update' method in the internal library requires a unique identifier in order to find
            // the record and updated it.  This creates a description of a parameter that identifies the primary key for the
            // internal interface.  By the time it is used in the call to the internal 'Update' method, the record on which this
            // parameter is based must be found using the unique key passed to the external interface.  That key to the external
            // method does not need to be the primary key, while for the internal method, this is a requirement.
            InternalParameterItem primaryKeyParameterItem = new InternalParameterItem();
            List <CodeExpression> primaryKeyExpressions   = new List <CodeExpression>();

            foreach (ColumnSchema columnSchema in tableSchema.PrimaryKey.Columns)
            {
                primaryKeyExpressions.Add(new CodePropertyReferenceExpression(uniqueConstraintParameterItem.CodeVariableReferenceExpression, columnSchema.Name));
            }
            primaryKeyParameterItem.Expression = new CodeArrayCreateExpression(new CodeGlobalTypeReference(typeof(System.Object)), primaryKeyExpressions.ToArray());
            primaryKeyParameterItem.Name       = string.Format("{0}Key", CommonConversion.ToCamelCase(tableSchema.Name));
            this.InternalParameters.Add(primaryKeyParameterItem.Name, primaryKeyParameterItem);

            // This will make sure that all the constraints on a table are satisfied by the list of parameters.
            foreach (ForeignKeyConstraintSchema foreignKeyConstraintSchema in tableSchema.ForeignKeyConstraintSchemas)
            {
                // No attempt will be made to resolve complex, redundant constraints as the same data can be found from simpler
                // ones.  The presence of the redundant, complex foreign keys will only confuse the interface.
                if (foreignKeyConstraintSchema.IsRedundant)
                {
                    continue;
                }

                // This item describes an input parameter that uses one or more external identifiers as a keys to a parent
                // table.
                ForeignKeyConstraintParameterItem foreignKeyConstraintParameterItem = new ForeignKeyConstraintParameterItem();
                foreignKeyConstraintParameterItem.ActualDataType             = typeof(System.Object[]);
                foreignKeyConstraintParameterItem.Description                = string.Format("An optional unique key for the parent {0} record.", foreignKeyConstraintSchema.RelatedTable);
                foreignKeyConstraintParameterItem.DeclaredDataType           = typeof(System.Object[]);
                foreignKeyConstraintParameterItem.FieldDirection             = FieldDirection.In;
                foreignKeyConstraintParameterItem.ForeignKeyConstraintSchema = foreignKeyConstraintSchema;
                foreignKeyConstraintParameterItem.IsNullable = true;
                foreignKeyConstraintParameterItem.Name       = string.Format("{0}Key", CommonConversion.ToCamelCase(foreignKeyConstraintSchema.RelatedTable.Name));
                if (!foreignKeyConstraintSchema.IsDistinctPathToParent)
                {
                    foreignKeyConstraintParameterItem.Name += "By";
                    foreach (ColumnSchema columnSchema in foreignKeyConstraintSchema.Columns)
                    {
                        foreignKeyConstraintParameterItem.Name += columnSchema.Name;
                    }
                }
                this.ExternalParameterItems.Add(foreignKeyConstraintParameterItem.Name, foreignKeyConstraintParameterItem);

                // This constructs a mapping between the multi-element key that is provided to the external method and the single
                // parameter that is needed by the internal method.
                foreach (ColumnSchema columnSchema in foreignKeyConstraintSchema.Columns)
                {
                    // This creates an intermediate variable to hold a value collected from the foreign keys.
                    ForeignKeyVariableItem foreignKeyVariableItem = new ForeignKeyVariableItem();
                    foreignKeyVariableItem.ColumnSchema = columnSchema;
                    foreignKeyVariableItem.DataType     = typeof(System.Object);
                    foreignKeyVariableItem.Expression   = new CodeRandomVariableReferenceExpression();
                    foreignKeyConstraintParameterItem.ForeignKeyVariables.Add(foreignKeyVariableItem);

                    // This creates an internal parameter for the update method from the elements of the foreign key.
                    InternalParameterItem internalParameterItem = new InternalParameterItem();
                    internalParameterItem.ColumnSchema = columnSchema;
                    internalParameterItem.Expression   = foreignKeyVariableItem.Expression;
                    internalParameterItem.Name         = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.InternalParameters.Add(internalParameterItem.Name, internalParameterItem);

                    // Any items resolved using foreign constraints are removed from the list of input parameters.  The key is the
                    // means of evaluating the column's value and it can't be added directly from the outside world.
                    columnList.Remove(columnSchema);
                }
            }

            // At this point, all the parameters that need to be resolved with foreign keys have been added to the list of
            // parameters and their child columns have been removed from the list of input parameters.  This will add the remaining
            // items to the list of external and internal parameters.  Note that the RowVersion is not useful for an external
            // interface and is removed automatically from the external parameter list.
            foreach (ColumnSchema columnSchema in columnList)
            {
                // If a column requires special processing, it is not handled with the rest of the parameters.
                bool isOrdinaryColumn = true;

                // Since the external world doesn't have access to the row versions, they must be removed from the input
                // parameters.  A mechanism is constructed internal to the method to defeat the optimistic concurrency checking.
                if (columnSchema.IsRowVersion)
                {
                    isOrdinaryColumn = false;
                    InternalParameterItem internalParameterItem = new InternalParameterItem();
                    internalParameterItem.Expression = new CodePropertyReferenceExpression(uniqueConstraintParameterItem.CodeVariableReferenceExpression, columnSchema.Name);
                    internalParameterItem.Name       = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.InternalParameters.Add(internalParameterItem.Name, internalParameterItem);
                }

                // Ordinary parameters are passed from the external caller to the internal methods without modification or
                // interpretation.
                if (isOrdinaryColumn)
                {
                    // This parameter is exposed in the external method to the outside world.
                    SimpleParameterItem simpleParameterItem = new SimpleParameterItem();
                    simpleParameterItem.ActualDataType   = columnSchema.DataType;
                    simpleParameterItem.ColumnSchema     = columnSchema;
                    simpleParameterItem.DeclaredDataType = typeof(System.Object);
                    simpleParameterItem.Description      = string.Format("The optional value for the {0} column.", CommonConversion.ToCamelCase(columnSchema.Name));
                    simpleParameterItem.FieldDirection   = FieldDirection.In;
                    simpleParameterItem.Name             = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.ExternalParameterItems.Add(simpleParameterItem.Name, simpleParameterItem);

                    // These parameters are used for the internal 'Update' method.
                    InternalParameterItem internalParameterItem = new InternalParameterItem();
                    internalParameterItem.Expression = new CodeVariableReferenceExpression(CommonConversion.ToCamelCase(columnSchema.Name));
                    internalParameterItem.Name       = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.InternalParameters.Add(internalParameterItem.Name, internalParameterItem);
                }
            }

            // A configuration identifier is is required on all interface methods except the 'Configuration' table.  While not
            // every method requires the resolution of multiple external keys, the decision to add this for column was made for
            // consistency.  The idea that the external interface shouldn't break if a new unique key is added to the hierarchy.
            // This means more work up front to configure the external interfaces, but it means less work later on as the relations
            // and schema change.
            if (!this.ExternalParameterItems.ContainsKey("configurationId"))
            {
                ExternalParameterItem externalParameterItem = new ExternalParameterItem();
                externalParameterItem.ActualDataType   = typeof(String);
                externalParameterItem.DeclaredDataType = typeof(String);
                externalParameterItem.Description      = "Selects a configuration of unique indices used to resolve external identifiers.";
                externalParameterItem.FieldDirection   = FieldDirection.In;
                externalParameterItem.IsNullable       = false;
                externalParameterItem.Name             = "configurationId";
                this.ExternalParameterItems.Add(externalParameterItem.Name, externalParameterItem);
            }
        }
        /// <summary>
        /// Creates an object that translates the parameters used for external methods to parameters for internal methods.
        /// </summary>
        public DestroyExParameterMatrix(TableSchema tableSchema)
        {
            // Initialize the object.
            this.ExternalParameterItems = new SortedList <string, ExternalParameterItem>();
            this.InternalParameters     = new SortedList <string, InternalParameterItem>();

            // Place all the columns of the table in a list that will drive the creation of parameters.  Those that columns that
            // are native to this table will be left in the list.  Those columns that are part of a foreign constraint will be
            // replace in the final argument list with internal values that can be used to look up the actual value.
            List <ColumnSchema> columnList = new List <ColumnSchema>();

            foreach (KeyValuePair <string, ColumnSchema> keyValuePair in tableSchema.Columns)
            {
                columnList.Add(keyValuePair.Value);
            }

            // Every set of parameters to the 'Destroy' method from an external interface requires a unique identifier in order to
            // find the record and destroy it.  This unique identifier can use any of the UniqueConstraints on the table in order to
            // find the record.  This creates a description of that parameter for the external method.
            UniqueConstraintParameterItem uniqueConstraintParameterItem = new UniqueConstraintParameterItem();

            uniqueConstraintParameterItem.ActualDataType   = typeof(System.Object[]);
            uniqueConstraintParameterItem.DeclaredDataType = typeof(System.Object[]);
            uniqueConstraintParameterItem.Description      = string.Format("A required unique key for the {0} record.", tableSchema.Name);
            uniqueConstraintParameterItem.FieldDirection   = FieldDirection.In;
            uniqueConstraintParameterItem.Name             = string.Format("{0}Key", CommonConversion.ToCamelCase(tableSchema.Name));
            uniqueConstraintParameterItem.CodeVariableReferenceExpression = new CodeRandomVariableReferenceExpression();
            this.ExternalParameterItems.Add(uniqueConstraintParameterItem.Name, uniqueConstraintParameterItem);

            // Every set of parameters to the 'Destroy' method in the internal library requires a unique identifier in order to find
            // the record and destroyd it.  This creates a description of a parameter that identifies the primary key for the
            // internal interface.  By the time it is used in the call to the internal 'Destroy' method, the record on which this
            // parameter is based must be found using the unique key passed to the external interface.  That key to the external
            // method does not need to be the primary key, while for the internal method, this is a requirement.
            InternalParameterItem primaryKeyParameterItem = new InternalParameterItem();
            List <CodeExpression> primaryKeyExpressions   = new List <CodeExpression>();

            foreach (ColumnSchema columnSchema in tableSchema.PrimaryKey.Columns)
            {
                primaryKeyExpressions.Add(new CodePropertyReferenceExpression(uniqueConstraintParameterItem.CodeVariableReferenceExpression, columnSchema.Name));
            }
            primaryKeyParameterItem.Expression = new CodeArrayCreateExpression(new CodeGlobalTypeReference(typeof(System.Object)), primaryKeyExpressions.ToArray());
            primaryKeyParameterItem.Name       = string.Format("{0}Key", CommonConversion.ToCamelCase(tableSchema.Name));
            this.InternalParameters.Add(primaryKeyParameterItem.Name, primaryKeyParameterItem);

            // At this point, all the parameters that need to be resolved with foreign keys have been added to the list of
            // parameters and their child columns have been removed from the list of input parameters.  This will add the remaining
            // items to the list of external and internal parameters.  Note that the RowVersion is not useful for an external
            // interface and is removed automatically from the external parameter list.
            foreach (ColumnSchema columnSchema in columnList)
            {
                // Since the external world doesn't have access to the row versions, they must be removed from the input
                // parameters.  A mechanism is constructed internal to the method to defeat the optimistic concurrency checking.
                if (columnSchema.IsRowVersion)
                {
                    InternalParameterItem internalParameterItem = new InternalParameterItem();
                    internalParameterItem.Expression = new CodePropertyReferenceExpression(uniqueConstraintParameterItem.CodeVariableReferenceExpression, columnSchema.Name);
                    internalParameterItem.Name       = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.InternalParameters.Add(internalParameterItem.Name, internalParameterItem);
                }
            }

            // A configuration identifier is is required on all interface methods except the 'Configuration' table.  While not
            // every method requires the resolution of multiple external keys, the decision to add this for column was made for
            // consistency.  The idea that the external interface shouldn't break if a new unique key is added to the hierarchy.
            // This means more work up front to configure the external interfaces, but it means less work later on as the relations
            // and schema change.
            if (!this.ExternalParameterItems.ContainsKey("configurationId"))
            {
                ExternalParameterItem externalParameterItem = new ExternalParameterItem();
                externalParameterItem.ActualDataType   = typeof(String);
                externalParameterItem.DeclaredDataType = typeof(String);
                externalParameterItem.Description      = "Selects a configuration of unique indices used to resolve external identifiers.";
                externalParameterItem.FieldDirection   = FieldDirection.In;
                externalParameterItem.IsNullable       = false;
                externalParameterItem.Name             = "configurationId";
                this.ExternalParameterItems.Add(externalParameterItem.Name, externalParameterItem);
            }
        }
        /// <summary>
        /// Creates an object that translates the parameters used for external methods to parameters for internal methods.
        /// </summary>
        public CreateExParameterMatrix(TableSchema tableSchema)
        {
            // Initialize the object.
            this.ExternalParameterItems = new SortedList <string, ExternalParameterItem>();
            this.CreateParameterItems   = new SortedList <string, InternalParameterItem>();
            this.UpdateParameterItems   = new SortedList <string, InternalParameterItem>();

            // Place all the columns of the table in a list that will drive the creation of parameters.  Those that columns that
            // are native to this table will be left in the list.  Those columns that are part of a foreign constraint will be
            // replace in the final argument list with internal values that can be used to look up the actual value.
            List <ColumnSchema> columnList = new List <ColumnSchema>();

            foreach (KeyValuePair <string, ColumnSchema> keyValuePair in tableSchema.Columns)
            {
                columnList.Add(keyValuePair.Value);
            }

            // The 'CreateEx' method doesn't have an explicit unique key in the parameter list like the 'Update' and 'Delete', so
            // there's no explicit input parameter to which to associate the unique row that is the target of the operation.  This
            // creates an implicit unique index which will be resolved using the explicit input parameters to this method.
            this.RowExpression       = new CodeRandomVariableReferenceExpression();
            this.UniqueKeyExpression = new CodeRandomVariableReferenceExpression();

            // Every internal update method requires a primary key specification.  This key is created from the primary key columns
            // of the existing row.  While it may seem redundant to find a row before calling the internal method to update that
            // same row, one of the primary purposes of the external method is to short-circuit the optimistic concurrency
            // checking.  To do that, the row version from the existing row is required.  The same row that provides the current
            // row version also provides the unique key to call the internal method.  However, Any one of the unique constraints
            // associated with the table can be used from the external interface to look up the row before the internal method is
            // called.  This makes it possible to change the primary key of a given column which is a requirement of a true
            // relational database model.  Said differently, this parameter item acts like the 'WHERE' clause in an SQL statement.
            InternalParameterItem primaryKeyParameterItem = new InternalParameterItem();
            List <CodeExpression> primaryKeyExpressions   = new List <CodeExpression>();

            foreach (ColumnSchema columnSchema in tableSchema.PrimaryKey.Columns)
            {
                primaryKeyExpressions.Add(new CodePropertyReferenceExpression(this.RowExpression, columnSchema.Name));
            }
            primaryKeyParameterItem.Expression = new CodeArrayCreateExpression(new CodeGlobalTypeReference(typeof(System.Object)), primaryKeyExpressions.ToArray());
            primaryKeyParameterItem.Name       = string.Format("{0}Key", CommonConversion.ToCamelCase(tableSchema.Name));
            this.UpdateParameterItems.Add(primaryKeyParameterItem.Name, primaryKeyParameterItem);

            // This will make sure that all the constraints on a table are satisfied by the list of parameters.
            foreach (ForeignKeyConstraintSchema foreignKeyConstraintSchema in tableSchema.ForeignKeyConstraintSchemas)
            {
                // No attempt will be made to resolve complex, redundant constraints as the same data can be found from simpler
                // ones.  The presence of the redundant, complex foreign keys will only confuse the interface.
                if (foreignKeyConstraintSchema.IsRedundant)
                {
                    continue;
                }

                // This item describes an input parameter that uses one or more external identifiers as a keys to parent tables.
                // Those parent tables provide the actual values that are used to update the record.  This allows an external
                // interface to use external identifiers from related tables.
                ForeignKeyConstraintParameterItem foreignKeyConstraintParameterItem = new ForeignKeyConstraintParameterItem();

                // This is the foreign constraint that is to be resolved with this parameter.
                foreignKeyConstraintParameterItem.ForeignKeyConstraintSchema = foreignKeyConstraintSchema;
                foreignKeyConstraintParameterItem.ActualDataType             = typeof(System.Object[]);
                foreignKeyConstraintParameterItem.DeclaredDataType           = typeof(System.Object[]);
                foreignKeyConstraintParameterItem.Description = foreignKeyConstraintParameterItem.IsNullable ?
                                                                string.Format("An optional unique key for the parent {0} record.", foreignKeyConstraintSchema.RelatedTable) :
                                                                string.Format("A required unique key for the parent {0} record.", foreignKeyConstraintSchema.RelatedTable);
                foreignKeyConstraintParameterItem.FieldDirection = FieldDirection.In;
                foreignKeyConstraintParameterItem.Name           = string.Format("{0}Key", CommonConversion.ToCamelCase(foreignKeyConstraintSchema.RelatedTable.Name));
                if (!foreignKeyConstraintSchema.IsDistinctPathToParent)
                {
                    foreignKeyConstraintParameterItem.Name += "By";
                    foreach (ColumnSchema columnSchema in foreignKeyConstraintSchema.Columns)
                    {
                        foreignKeyConstraintParameterItem.Name += columnSchema.Name;
                    }
                }
                foreignKeyConstraintParameterItem.IsNullable = true;
                foreach (ColumnSchema columnSchema in foreignKeyConstraintSchema.Columns)
                {
                    if (!columnSchema.IsNullable)
                    {
                        foreignKeyConstraintParameterItem.IsNullable = false;
                    }
                }
                this.ExternalParameterItems.Add(foreignKeyConstraintParameterItem.Name, foreignKeyConstraintParameterItem);

                // This constructs a mapping between the foreign key parameter that is provided to the external method and the
                // individual parameters that are needed by the internal method.
                foreach (ColumnSchema columnSchema in foreignKeyConstraintSchema.Columns)
                {
                    // This creates an intermediate variable to hold a value collected from the foreign keys.  When creating an
                    // object, the required values are strongly typed.  Optional columns and columns with defaults are defined as
                    // generic types so they can be nulled if no cooresponding value is provided.
                    ForeignKeyVariableItem foreignKeyVariableItem = new ForeignKeyVariableItem();
                    foreignKeyVariableItem.ColumnSchema = columnSchema;
                    foreignKeyVariableItem.DataType     = columnSchema.IsNullable || columnSchema.DefaultValue != DBNull.Value ? typeof(System.Object) : columnSchema.DataType;
                    foreignKeyVariableItem.Expression   = new CodeRandomVariableReferenceExpression();
                    foreignKeyConstraintParameterItem.ForeignKeyVariables.Add(foreignKeyVariableItem);

                    // This creates an internal parameter for the 'Create' method from the elements of the foreign key.
                    InternalParameterItem createParameterItem = new InternalParameterItem();
                    createParameterItem.ColumnSchema = columnSchema;
                    createParameterItem.Expression   = foreignKeyVariableItem.Expression;
                    createParameterItem.Name         = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.CreateParameterItems.Add(createParameterItem.Name, createParameterItem);

                    // This creates an internal parameter for the 'Update' method from the elements of the foreign key.
                    InternalParameterItem updateParameterItem = new InternalParameterItem();
                    updateParameterItem.ColumnSchema = columnSchema;
                    updateParameterItem.Expression   = foreignKeyVariableItem.Expression;
                    updateParameterItem.Name         = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.UpdateParameterItems.Add(updateParameterItem.Name, updateParameterItem);

                    // Any items resolved using foreign constraints are removed from the list of input parameters.  The key is the
                    // means of evaluating the column's value and it can't be added directly from the outside world.
                    columnList.Remove(columnSchema);
                }
            }

            // At this point, all the parameters that need to be resolved with foreign keys have been added to the list of external
            // and internal parameters and their related columns have been removed from the list of input parameters.  This next
            // block will add the remaining items to the list of external and internal parameters.
            foreach (ColumnSchema columnSchema in columnList)
            {
                // If a column requires special processing, it is not handled with the rest of the parameters.
                bool isOrdinaryColumn = true;

                // Since the external world doesn't have access to the row versions, they must be removed from the input
                // parameters.  A mechanism is constructed internal to the method to defeat the optimistic concurrency checking.
                if (columnSchema.IsRowVersion)
                {
                    // This is no ordinary column.
                    isOrdinaryColumn = false;

                    // This parameters is used for the internal 'Update' methods to defeat the optimistic concurrency checking.  It is not
                    // part of the 'Create' interface.
                    InternalParameterItem internalParameterItem = new InternalParameterItem();
                    internalParameterItem.ColumnSchema = columnSchema;
                    internalParameterItem.Expression   = new CodePropertyReferenceExpression(this.RowExpression, "RowVersion");
                    internalParameterItem.Name         = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.UpdateParameterItems.Add(internalParameterItem.Name, internalParameterItem);
                }

                // AutoIncremented columns can only be specified as output parameters.
                if (columnSchema.IsAutoIncrement)
                {
                    // This is no ordinary column.
                    isOrdinaryColumn = false;

                    // Create an outpt parameter for the AutoIncremented values as the values are always generated by the middle
                    // tier.
                    SimpleParameterItem simpleParameterItem = new SimpleParameterItem();
                    simpleParameterItem.ActualDataType   = columnSchema.DataType;
                    simpleParameterItem.ColumnSchema     = columnSchema;
                    simpleParameterItem.DeclaredDataType = columnSchema.DataType;
                    simpleParameterItem.Description      = string.Format("The output value for the {0} column.", columnSchema.Name);
                    simpleParameterItem.Name             = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.ExternalParameterItems.Add(simpleParameterItem.Name, simpleParameterItem);

                    // Note that when an object with an AutoIncrement column is added, the column is specified as an output
                    // parameter.
                    InternalParameterItem createParameterItem = new InternalParameterItem();
                    createParameterItem.ColumnSchema = columnSchema;
                    createParameterItem.Expression   = new CodeDirectionExpression(FieldDirection.Out, new CodeArgumentReferenceExpression(simpleParameterItem.Name));
                    createParameterItem.Name         = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.CreateParameterItems.Add(createParameterItem.Name, createParameterItem);

                    // The AutoIncrement column is a simple input parameter when the record is updated.
                    InternalParameterItem updateParameterItem = new InternalParameterItem();
                    updateParameterItem.ColumnSchema = columnSchema;
                    updateParameterItem.Expression   = new CodeArgumentReferenceExpression(simpleParameterItem.Name);
                    updateParameterItem.Name         = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.UpdateParameterItems.Add(updateParameterItem.Name, updateParameterItem);
                }

                // Ordinary parameters are passed from the external caller to the internal methods without modification or
                // interpretation.
                if (isOrdinaryColumn)
                {
                    // Create a simple input parameter from the column information.  The only complication is whether the value
                    // must be provided by the caller or is optional.
                    SimpleParameterItem simpleParameterItem = new SimpleParameterItem();
                    bool isOptional = columnSchema.IsNullable || columnSchema.DefaultValue != DBNull.Value;
                    simpleParameterItem.ActualDataType   = columnSchema.DataType;
                    simpleParameterItem.ColumnSchema     = columnSchema;
                    simpleParameterItem.DeclaredDataType = isOptional ? typeof(System.Object) : columnSchema.DataType;
                    simpleParameterItem.Description      = string.Format("The {0} value for the {1} column.", isOptional ? "optional" : "required", CommonConversion.ToCamelCase(columnSchema.Name));
                    simpleParameterItem.FieldDirection   = FieldDirection.In;
                    simpleParameterItem.Name             = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.ExternalParameterItems.Add(simpleParameterItem.Name, simpleParameterItem);

                    // This is how the parameter will be interpreted by the 'Insert' and 'Update' internal methods.
                    InternalParameterItem internalParameterItem = new InternalParameterItem();
                    internalParameterItem.ColumnSchema = columnSchema;
                    internalParameterItem.Expression   = new CodeArgumentReferenceExpression(CommonConversion.ToCamelCase(columnSchema.Name));
                    internalParameterItem.Name         = CommonConversion.ToCamelCase(columnSchema.Name);
                    this.CreateParameterItems.Add(internalParameterItem.Name, internalParameterItem);
                    this.UpdateParameterItems.Add(internalParameterItem.Name, internalParameterItem);
                }
            }

            // A configuration identifier is is required on all interface methods except the 'Configuration' table.  While not
            // every method requires the resolution of multiple external keys, the decision to add this for column was made for
            // consistency.  The idea that the external interface shouldn't break if a new unique key is added to the hierarchy.
            // This means more work up front to configure the external interfaces, but it means less work later on as the relations
            // and schema change.
            if (tableSchema.Name != "Configuration")
            {
                ExternalParameterItem externalParameterItem = new ExternalParameterItem();
                externalParameterItem.ActualDataType   = typeof(String);
                externalParameterItem.DeclaredDataType = typeof(String);
                externalParameterItem.Description      = "Selects a configuration of unique indices used to resolve external identifiers.";
                externalParameterItem.FieldDirection   = FieldDirection.In;
                externalParameterItem.IsNullable       = false;
                externalParameterItem.Name             = "configurationId";
                this.ExternalParameterItems.Add(externalParameterItem.Name, externalParameterItem);
            }
        }