private List <Dictionary <string, object> > GetJson(DataTable dt) { var rows = new List <Dictionary <string, object> >(); var reportColumns = new IReportColumn[dt.Columns.Count]; for (int i = 0; i < dt.Columns.Count; i++) { reportColumns[i] = _reportColumns.Single(x => x.SqlAlias == dt.Columns[i].ColumnName); } foreach (DataRow dr in dt.Rows) { var row = new Dictionary <string, object>(); for (int j = 0; j < dt.Columns.Count; j++) { string value = ""; if (dr[j] != null) { value = reportColumns[j].OutputValueTransformation(dr[j].ToString()); } row.Add(dt.Columns[j].ColumnName, value); } rows.Add(row); } return(rows); }
public void AddReportColumn(IReportColumn reportColumn) { if (reportColumn == null) { throw new ArgumentNullException("reportColumn"); } if (_reportColumns.Any(x => x.SqlAlias == reportColumn.SqlAlias)) { throw new ReportException(string.Format("Report model can not contain columns with equal SqlAliaces: {0}", reportColumn.SqlAlias)); } _reportColumns.Add(reportColumn); }
/// <summary> /// Insert, at the correct location, the specified column into the columns list /// Need to ensure that array columns are kept in order. /// </summary> /// <param name="column">The column to insert.</param> /// <param name="flattenedColumns">The returned flattened columns.</param> private static void InsertColumn(IReportColumn column, List <IReportColumn> flattenedColumns) { int indexOfBracket = column.Name.IndexOf('('); if (indexOfBracket != -1) { // find the last column that has a name is identical up to bracketed value. string namePrefixToMatch = column.Name.Substring(0, indexOfBracket); int indexToInsertAfter = flattenedColumns.FindLastIndex(col => col.Name.StartsWith(namePrefixToMatch)); if (indexToInsertAfter != -1) { flattenedColumns.Insert(indexToInsertAfter + 1, column); return; } } flattenedColumns.Add(column); }
public void BuildQuery_GivenMultipleReportColumns_ValidSqlCommand() { var reportColumns = new IReportColumn[] { new ReportColumn { Title = "First Field", SqlValueExpression = "TestTable.fField", }, new ReportColumn { Title = "Second Field", SqlValueExpression = "TestTable.sField", }, }; SqlCommand command = queryBuilder.BuildQuery(reportColumns, Enumerable.Empty <IReportFilter>(), _dataSource); Assert.That(command.CommandText, Is.EqualTo("SELECT (TestTable.fField) AS FirstField, (TestTable.sField) AS SecondField FROM TestTable")); Assert.That(command.Parameters.Count, Is.EqualTo(0)); }
/// <summary> /// Get the value of a variable or model. /// </summary> /// <param name="namePath">The name of the object to return</param> /// <param name="relativeTo">The model calling this method</param> /// <param name="ignoreCase">If true, ignore case when searching for the object or property</param> /// <returns>The found object or null if not found</returns> public IVariable GetInternal(string namePath, Model relativeTo, bool ignoreCase = false) { Model relativeToModel = relativeTo; string cacheKey = namePath; StringComparison compareType = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; // Look in cache first. object value = GetFromCache(cacheKey, relativeToModel); if (value != null) { return(value as IVariable); } IVariable returnVariable = null; if (namePath == null || namePath.Length == 0) { return(null); } else if (namePath[0] != '.' && namePath.Replace("()", "").IndexOfAny("(+*/".ToCharArray()) != -1) { // expression - need a better way of detecting an expression returnVariable = new VariableExpression(namePath, relativeToModel); } else { // Remove a square bracketed model name and change our relativeTo model to // the referenced model. if (namePath.StartsWith("[")) { int posCloseBracket = namePath.IndexOf(']'); if (posCloseBracket == -1) { return(null); } string modelName = namePath.Substring(1, posCloseBracket - 1); namePath = namePath.Remove(0, posCloseBracket + 1); Model foundModel = relativeToModel.FindInScope(modelName) as Model; if (foundModel == null) { // Didn't find a model with a name matching the square bracketed string so // now try and look for a model with a type matching the square bracketed string. Type[] modelTypes = GetTypeWithoutNameSpace(modelName); if (modelTypes.Length == 1) { foundModel = relativeToModel.FindAllInScope().FirstOrDefault(m => modelTypes[0].IsAssignableFrom(m.GetType())) as Model; } } if (foundModel == null) { return(null); } else { relativeToModel = foundModel; } } else if (namePath.StartsWith(".")) { // Absolute path Model root = relativeToModel; while (root.Parent != null) { root = root.Parent as Model; } relativeToModel = root; int posPeriod = namePath.IndexOf('.', 1); if (posPeriod == -1) { // Path starts with a . and only contains a single period. // If name matches then no problem. Otherwise we need to return null. posPeriod = namePath.IndexOf('.'); if (namePath.Remove(0, posPeriod) == relativeToModel.Name) { posPeriod = namePath.Length; } } namePath = namePath.Remove(0, posPeriod); if (namePath.StartsWith(".")) { namePath = namePath.Remove(0, 1); } } else { // Try a report column. foreach (Report report in relativeTo.FindAllInScope <Report>()) { IReportColumn column = report.Columns?.Find(c => c.Name == namePath); if (column != null) { return(new VariableObject(column.GetValue(0))); } } } // Now walk the series of '.' separated path bits, assuming the path bits // are child models. Stop when we can't find the child model. string[] namePathBits = namePath.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); if (namePathBits.Length == 0 && !string.IsNullOrEmpty(namePath)) { throw new Exception($"Invalid variable name: '{cacheKey}'"); } int i; for (i = 0; i < namePathBits.Length; i++) { IModel localModel = relativeToModel.Children.FirstOrDefault(m => m.Name.Equals(namePathBits[i], compareType)); if (localModel == null) { // Allow for the possibility that the first path element may point to // the starting parent model, rather than to a child within that model if ((i == 0) && relativeToModel.Name.Equals(namePathBits[i], compareType)) { continue; } else { break; } } else { relativeToModel = localModel as Model; } } // At this point there are only 2 possibilities. We have encountered a // PropertyInfo or the path is invalid. // We now need to loop through the remaining path bits and keep track of each // section of the path as each section will have to be evaulated everytime a // a get is done for this path. // The variable 'i' will point to the name path that cannot be found as a model. object relativeToObject = relativeToModel; List <IVariable> properties = new List <IVariable>(); properties.Add(new VariableObject(relativeToModel)); for (int j = i; j < namePathBits.Length; j++) { // look for an array specifier e.g. sw[2] string arraySpecifier = null; if (namePathBits[j].Contains("[")) { arraySpecifier = StringUtilities.SplitOffBracketedValue(ref namePathBits[j], '[', ']'); } // Look for either a property or a child model. IModel localModel = null; if (relativeToObject == null) { return(null); } PropertyInfo propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j]); if (propertyInfo == null && ignoreCase) // If not found, try using a case-insensitive search { propertyInfo = relativeToObject.GetType().GetProperty(namePathBits[j], BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase); } if (relativeToObject is IFunction && namePathBits[j] == "Value()") { MethodInfo method = relativeToModel.GetType().GetMethod("Value"); properties.Add(new VariableMethod(relativeToModel, method)); } else if (propertyInfo == null && relativeToObject is Model) { // Not a property, may be a child model. localModel = (relativeToObject as IModel).Children.FirstOrDefault(m => m.Name.Equals(namePathBits[i], compareType)); if (localModel == null) { return(null); } properties.Add(new VariableObject(localModel)); relativeToObject = localModel; } else if (propertyInfo != null) { VariableProperty property = new VariableProperty(relativeToObject, propertyInfo, arraySpecifier); properties.Add(property); relativeToObject = property.Value; } else if (relativeToObject is IList) { // Special case: we are trying to get a property of an array(IList). In this case // we want to return the property value for all items in the array. VariableProperty property = new VariableProperty(relativeToObject, namePathBits[j], arraySpecifier); properties.Add(property); relativeToObject = property.Value; } else { return(null); } } // We now have a list of IVariable instances that can be evaluated to // produce a return value for the given path. Wrap the list into an IVariable. returnVariable = new VariableComposite(namePath, properties); } // Add variable to cache. AddToCache(cacheKey, relativeTo, returnVariable); return(returnVariable); }
/// <summary> /// 'Flatten' the object passed in, into a list of columns ready to be added /// to a data table. /// </summary> /// <param name="name">Column name where the value came from.</param> /// <param name="units">Units of measurement; null if not applicable</param> /// <param name="value">The object to be analyzed and flattened</param> /// <param name="rowIndex">The row index of the specified value.</param> /// <param name="flattenedColumns">The returned flattened columns.</param> /// <returns>The list of values that can be written to a data table</returns> private static void FlattenValue(string name, string units, object value, int rowIndex, List <IReportColumn> flattenedColumns) { if (value == null || value.GetType() == typeof(DateTime) || value.GetType() == typeof(string) || !value.GetType().IsClass) { // Scalar IReportColumn flattenedColumn = flattenedColumns.Find(col => col.Name == name); if (flattenedColumn == null) { flattenedColumn = new ReportColumnWithValues(name, units); InsertColumn(flattenedColumn, flattenedColumns); } // Ensure all columns have the correct number of values. foreach (IReportColumn column in flattenedColumns) { if (column is ReportColumnConstantValue) { } else { while (column.Values.Count <= rowIndex) { column.Values.Add(null); } } } flattenedColumn.Values[rowIndex] = value; } else if (value.GetType().IsArray) { // Array Array array = value as Array; for (int columnIndex = 0; columnIndex < array.Length; columnIndex++) { string heading = name; heading += "(" + (columnIndex + 1).ToString() + ")"; object arrayElement = array.GetValue(columnIndex); FlattenValue(heading, units, arrayElement, rowIndex, flattenedColumns); // recursion } } else if (value.GetType().GetInterface("IList") != null) { // List IList array = value as IList; for (int columnIndex = 0; columnIndex < array.Count; columnIndex++) { string heading = name; heading += "(" + (columnIndex + 1).ToString() + ")"; object arrayElement = array[columnIndex]; FlattenValue(heading, units, arrayElement, rowIndex, flattenedColumns); // recursion } } else { // A struct or class foreach (PropertyInfo property in ReflectionUtilities.GetPropertiesSorted(value.GetType(), BindingFlags.Instance | BindingFlags.Public)) { object[] attrs = property.GetCustomAttributes(true); string propUnits = null; bool ignore = false; foreach (object attr in attrs) { if (attr is XmlIgnoreAttribute) { ignore = true; continue; } Core.UnitsAttribute unitsAttr = attr as Core.UnitsAttribute; if (unitsAttr != null) { propUnits = unitsAttr.ToString(); } } if (ignore) { continue; } string heading = name + "." + property.Name; object classElement = property.GetValue(value, null); FlattenValue(heading, propUnits, classElement, rowIndex, flattenedColumns); } } }