/// <summary> /// Expand one attribute by building its path segments as dimension objects. /// Use the provided list of attributes for expansion recursively. This list essentially represents a schema. /// Also, adjust path names in special cases like empty name or simple structure. /// </summary> public void ExpandAttribute(List <ColumnAtt> attributes, List <DcColumn> columns) // Add and resolve attributes by creating dimension structure from FKs { ColumnAtt att = this; if (att.Segments.Count > 0) { return; // Already expanded (because of recursion) } bool isKey = !string.IsNullOrEmpty(att.RelationalPkName) || att.IsKey; if (string.IsNullOrEmpty(att.RelationalFkName)) // No FK - primitive column - end of recursion { // Find or create a primitive dim segment DcColumn seg = columns.FirstOrDefault(c => c.Input == att.Input && StringSimilarity.SameColumnName(((ColumnRel)c).RelationalFkName, att.RelationalFkName)); if (seg == null) { seg = new ColumnRel(att.RelationalColumnName, att.Input, att.Output, isKey, false); // Maybe copy constructor? ((ColumnRel)seg).RelationalFkName = att.RelationalFkName; columns.Add(seg); } att.InsertLast(seg); // add it to this attribute as a single segment } else { // There is FK - non-primitive column // Find target set and target attribute (name resolution) ColumnAtt tailAtt = attributes.FirstOrDefault(a => StringSimilarity.SameTableName(a.Input.Name, att.RelationalTargetTableName) && StringSimilarity.SameColumnName(a.Name, att.RelationalTargetColumnName)); DcTable gTab = tailAtt.Input; // Find or create a dim segment DcColumn seg = columns.FirstOrDefault(c => c.Input == att.Input && StringSimilarity.SameColumnName(((ColumnRel)c).RelationalFkName, att.RelationalFkName)); if (seg == null) { seg = new ColumnRel(att.RelationalFkName, att.Input, gTab, isKey, false); ((ColumnRel)seg).RelationalFkName = att.RelationalFkName; columns.Add(seg); } att.InsertLast(seg); // add it to this attribute as first segment // // Recursion. Expand tail attribute and add all segments from the tail attribute (continuation) // tailAtt.ExpandAttribute(attributes, columns); att.InsertLast(tailAtt); // Adjust name. How many attributes belong to the same FK as this attribute (FK composition) List <ColumnAtt> fkAtts = attributes.Where(a => a.Input == att.Input && StringSimilarity.SameColumnName(att.RelationalFkName, a.RelationalFkName)).ToList(); if (fkAtts.Count == 1) { seg.Name = att.RelationalColumnName; // Adjust name. For 1-column FK, name of the FK-dim is the column name (not the FK name) } } }
private void FillEntriesTypes() { // Change available target type lists for each source column without changing the list itself // If some current type is not present in the new schema then deselect this entry // If an entry is selected then it must have some type selected (it is error if an entry does not have a selected type) List <DcTable> targetTypes = GetSchemaTypes(); DcTable defaultType = null; foreach (DcTable table in targetTypes) { if (StringSimilarity.SameTableName(table.Name, "String")) { defaultType = table; break; } } foreach (ColumnMappingEntry entry in Entries) { entry.TargetTypes.Clear(); targetTypes.ForEach(x => entry.TargetTypes.Add(x)); // Now find a good new match for the current type // In fact, it should be done by some automatic matching procedure for inter-schema primitive matches // Target type has been selected. Try to find the same type if (entry.TargetType != null) { DcTable targetType = null; foreach (DcTable table in targetTypes) { if (StringSimilarity.SameTableName(table.Name, entry.TargetType.Name)) { targetType = table; break; // Found } } if (targetType != null) // Found { entry.TargetType = targetType; continue; } } // Either not selected or not found. Try to select the source type if (entry.Source.Output != null) { DcTable targetType = null; foreach (DcTable table in targetTypes) { if (StringSimilarity.SameTableName(table.Name, entry.Source.Output.Name)) { targetType = table; break; // Found } } if (targetType != null) // Found { entry.TargetType = targetType; continue; } } // Nothing helps entry.TargetType = defaultType; } }