/// <summary> /// Identify cache dependencies. /// </summary> private static void IdentifyCacheDependencies(Report report, ReportToQueryConverterSettings settings, StructuredQuery query) { // Tell the cache what entities were referenced to return // the StructuredQuery result using (CacheContext cacheContext = CacheContext.GetContext( )) { cacheContext.Entities.Add(report.Id); if (query.Conditions != null) { foreach (var condition in query.Conditions) { cacheContext.Entities.Add(condition.EntityId); if (condition.Expression != null) { cacheContext.Entities.Add(condition.Expression.EntityId); } } } if (query.OrderBy != null) { foreach (var orderBy in query.OrderBy) { if (orderBy.Expression != null) { cacheContext.Entities.Add(orderBy.Expression.EntityId); } } } if (query.SelectColumns != null) { foreach (var column in query.SelectColumns) { cacheContext.Entities.Add(column.EntityId); if (column.Expression != null) { cacheContext.Entities.Add(column.Expression.EntityId); } } } if (report.ReportOrderBys != null) { foreach (var orderBy in report.ReportOrderBys) { if (orderBy != null) { cacheContext.Entities.Add(orderBy.Id); } } } } StructuredQueryHelper.IdentifyStructureCacheDependencies(query, settings.ConditionsOnly); }
/// <summary> /// Convert a <see cref="Report" /> to a <see cref="StructuredQuery" />. /// </summary> /// <param name="report">The <see cref="Report" /> to convert. This cannot be null.</param> /// <param name="settings"></param> /// <returns> /// The converted report. /// </returns> /// <exception cref="System.ArgumentNullException">report</exception> public StructuredQuery Convert(Report report, ReportToQueryConverterSettings settings) { if (report == null) { throw new ArgumentNullException("report"); } if (settings == null) { settings = ReportToQueryConverterSettings.Default; } StructuredQuery result; CachingReportToQueryConverterValue cacheValue; CachingReportToQueryConverterKey key = new CachingReportToQueryConverterKey(report, settings); using (MessageContext msg = new MessageContext("Reports")) { // Check cache bool doConvert = !TryGetValue(key, msg, out cacheValue); // Check for force recalculation if (settings.RefreshCachedStructuredQuery) { msg.Append(() => "CachingReportToQueryConverter refreshed forced"); doConvert = true; } if (doConvert) { lock (_syncRoot) { using (CacheContext cacheContext = new CacheContext( )) { result = Converter.Convert(report, settings); cacheValue = new CachingReportToQueryConverterValue(result); Cache.Add(key, cacheValue); // Add the cache context entries to the appropriate CacheInvalidator _cacheInvalidator.AddInvalidations(cacheContext, key); } } } else if (CacheContext.IsSet( )) { // Add the already stored changes that should invalidate this cache // entry to any outer or containing cache contexts. using (CacheContext cacheContext = CacheContext.GetContext( )) { cacheContext.AddInvalidationsFor(_cacheInvalidator, key); } } } result = cacheValue.StructuredQuery; return(result); }
public FromEntityContext() { ReportNodeMap = new Dictionary <long, Guid>(); ReportNodeToEntityMap = new Dictionary <long, Entity>(); ReportColumnMap = new Dictionary <long, Guid>(); ColumnReferenceMap = new Dictionary <Guid, long>(); ReportExpressionMap = new Dictionary <Guid, long>(); InstanceExpressionMap = new Dictionary <long, ScalarExpression>(); ReportInvalidNodes = new Dictionary <long, string>(); ReportInvalidColumns = new Dictionary <long, string>(); ReportInvalidConditions = new Dictionary <long, string>(); Settings = new ReportToQueryConverterSettings( ); }
/// <summary> /// Convert a <see cref="Report" /> to a <see cref="StructuredQuery" />. /// </summary> /// <param name="report">The <see cref="Report" /> to convert. This cannot be null.</param> /// <param name="settings">Settings that control the converter behavior.</param> /// <returns> /// The converted report. /// </returns> /// <exception cref="System.ArgumentNullException">report</exception> public StructuredQuery Convert(Report report, ReportToQueryConverterSettings settings) { if (report == null) { throw new ArgumentNullException("report"); } if (settings == null) { settings = ReportToQueryConverterSettings.Default; } StructuredQuery query = StructuredQueryEntityHelper.ConvertReport(report, settings); IdentifyCacheDependencies(report, settings, query); return(query); }
/// <summary> /// Get the structured query, possibly from cache. /// </summary> /// <param name="report">The report to convert.</param> /// <param name="settings">The report run settings.</param> /// <param name="suppressPreload">True if we should suppress preloading.</param> /// <returns>The structured query.</returns> private StructuredQuery GetStructuredQuery(Model.Report report, ReportSettings settings, bool suppressPreload) { using (MessageContext msg = new MessageContext("Reports")) { StructuredQuery immutableStructuredQuery; StructuredQuery structuredQuery; bool useStructuredQueryCache = settings.UseStructuredQueryCache; ReportToQueryConverterSettings converterSettings = new ReportToQueryConverterSettings { SuppressPreload = suppressPreload, RefreshCachedStructuredQuery = settings.RefreshCachedStructuredQuery, SchemaOnly = settings.RequireSchemaMetadata }; if (settings != null && settings.UseStructuredQueryCache) { // don't allow mutations of cached copy immutableStructuredQuery = CachedReportToQueryConverter.Convert(report, converterSettings); } else { // don't allow mutations, just so we can log it correctly immutableStructuredQuery = NonCachedReportToQueryConverter.Convert(report, converterSettings); } structuredQuery = immutableStructuredQuery.DeepCopy( ); // so we can mutate it (in case we need to) // Logging msg.Append(() => new String('-', 50)); msg.Append(() => "GetStructuredQuery"); msg.Append(() => "suppressPreload = " + suppressPreload); msg.Append(() => "useStructuredQueryCache = " + useStructuredQueryCache); msg.Append(() => "Structured Query:\n" + StructuredQueryHelper.ToXml(immutableStructuredQuery)); msg.Append(() => new String('-', 50)); return(structuredQuery); } }
/// <summary> /// Converts a report into a structured query object. /// </summary> /// <param name="report">The report to convert</param> /// <param name="settings">Conversion settings.</param> /// <returns>The structured query.</returns> public static StructuredQuery ConvertReport(Report report, ReportToQueryConverterSettings settings) { if (report == null) { throw new ArgumentNullException(); } if (settings == null) { settings = ReportToQueryConverterSettings.Default; } Report reportToLoad = report; if (report.RootNode == null) { throw new ArgumentException(string.Format("Invalid report '{0}' to load the structured query", report.Id)); } // Preload the entities if (!settings.SuppressPreload) { ReportHelpers.PreloadQuery(reportToLoad.Id); } // Create context that contains information for conversion FromEntityContext fromEntityContext = new FromEntityContext { Report = reportToLoad, Settings = settings }; // Convet the report entity StructuredQuery sq = BuildStructuredQuery(fromEntityContext, settings); // Attach the report to the structured query (why??) sq.Report = report; return(sq); }
/// <summary> /// Build the actual structured query object. /// </summary> private static StructuredQuery BuildStructuredQuery(FromEntityContext context, ReportToQueryConverterSettings settings) { Report report = context.Report; using (Profiler.Measure("Report {0} to StructuredQuery", report.Id)) { StructuredQuery sq = new StructuredQuery(false); // Build up query starting with the root node using (Profiler.Measure("Query Tree")) { sq.RootEntity = BuildReportNode(report.RootNode, context); } // then the list of select columns using (Profiler.Measure("Columns")) { sq.SelectColumns = BuildSelectColumns(report.ReportColumns, context, settings); } // and then the list query conditions using (Profiler.Measure("Conditions")) { sq.Conditions = BuildQueryConditions(report.HasConditions, context, settings); } // finally build up the list of order by items sq.OrderBy = BuildOrderBy(report.ReportOrderBys, context, settings); // Hook up the references ResolveReferences(sq, report, context); // Report back any errors sq.InvalidReportInformation = new Dictionary <string, Dictionary <long, string> > { { "nodes", context.ReportInvalidNodes }, { "columns", context.ReportInvalidColumns }, { "conditions", context.ReportInvalidConditions } }; return(sq); } }
/// <summary> /// Builds the order by collection. /// </summary> /// <param name="reportOrderBys">The report order bys.</param> /// <param name="context">The context.</param> /// <param name="settings">Conversion settings.</param> /// <returns>List{OrderByItem}.</returns> internal static List <OrderByItem> BuildOrderBy(IEntityCollection <ReportOrderBy> reportOrderBys, FromEntityContext context, ReportToQueryConverterSettings settings) { List <OrderByItem> orderByItems = new List <OrderByItem>(); if (context.Settings.ConditionsOnly) { return(orderByItems); // don't bother converting them (e.g. for use in security) } foreach (ReportOrderBy reportOrderBy in reportOrderBys.OrderBy(rob => rob.OrderPriority)) { ScalarExpression expression = BuildExpression(reportOrderBy.OrderByExpression, context); if (expression == null) { EventLog.Application.WriteWarning("the order expression id: {0} is invalid.", reportOrderBy.OrderByExpression != null ? reportOrderBy.OrderByExpression.Id.ToString() : "null"); continue; } //the orderby expression is ColumnReferenceExpression, the referenced report column must exists if (context.ColumnReferenceMap.ContainsKey(expression.ExpressionId) && context.ReportColumnMap.ContainsKey(context.ColumnReferenceMap[expression.ExpressionId])) { OrderByItem orderByItem = new OrderByItem { Direction = reportOrderBy.ReverseOrder ?? false ? OrderByDirection.Descending : OrderByDirection.Ascending, Expression = expression }; orderByItems.Add(orderByItem); } } return(orderByItems); }
/// <summary> /// Builds the query conditions. /// </summary> /// <param name="reportConditions">The report conditions.</param> /// <param name="context">The context.</param> /// <param name="settings">Conversion settings.</param> /// <returns>List{QueryCondition}.</returns> internal static List <QueryCondition> BuildQueryConditions(IEntityCollection <ReportCondition> reportConditions, FromEntityContext context, ReportToQueryConverterSettings settings) { List <QueryCondition> queryConditions = new List <QueryCondition>(); foreach (ReportCondition reportCondition in reportConditions) { // TODO Sort out analyser field crap (i.e. hidden/display name etc)?? Do we need this?? ConditionType conditionType = ConditionType.Unspecified; if (reportCondition.Operator != null) { string[] conditionOperatorParts = reportCondition.Operator.Alias.Split(':'); string conditionOperator = conditionOperatorParts.Length == 2 ? conditionOperatorParts[1].Substring(4) : conditionOperatorParts[0].Substring(4); conditionType = (ConditionType)Enum.Parse(typeof(ConditionType), conditionOperator, true); } ScalarExpression scalarExpression = null; if (reportCondition.ConditionExpression == null) { context.ReportInvalidConditions[reportCondition.Id] = reportCondition.Name; if (!settings.SchemaOnly) { continue; } } else { scalarExpression = BuildExpression(reportCondition.ConditionExpression, context); if (scalarExpression == null) { EventLog.Application.WriteWarning("the condition expression id: {0} is invalid.", reportCondition.ConditionExpression.Id.ToString()); if (!settings.SchemaOnly) { continue; // TODO: log warning .. should we just abort the query if it refers to an invalid expression in a condition .. because it means we'll get more rows. } } } QueryCondition queryCondition = new QueryCondition { EntityId = reportCondition.Id, Operator = conditionType, Expression = scalarExpression }; BuildConditionParameter(reportCondition, queryCondition, context); queryConditions.Add(queryCondition); } return(queryConditions); }
/// <summary> /// Builds the select columns. /// </summary> /// <param name="reportColumns">The report columns.</param> /// <param name="context">The context.</param> /// <param name="settings">Conversion settings.</param> /// <returns>List{SelectColumn}.</returns> internal static List <SelectColumn> BuildSelectColumns(IEntityCollection <ReportColumn> reportColumns, FromEntityContext context, ReportToQueryConverterSettings settings) { List <SelectColumn> columns = new List <SelectColumn>(); // Order the columns based on position IEnumerable <ReportColumn> orderedReportColumns = reportColumns.OrderBy(rc => rc.ColumnDisplayOrder); foreach (ReportColumn orderedReportColumn in orderedReportColumns) { ScalarExpression scalarExpression = null; if (orderedReportColumn.ColumnExpression == null) { context.ReportInvalidColumns[orderedReportColumn.Id] = orderedReportColumn.Name; if (!settings.SchemaOnly) { continue; } } else { scalarExpression = BuildExpression(orderedReportColumn.ColumnExpression, context); if (scalarExpression == null) { EventLog.Application.WriteWarning("the column expression id: {0} is invalid.", orderedReportColumn.ColumnExpression.Id.ToString()); context.ReportInvalidColumns[orderedReportColumn.Id] = orderedReportColumn.Name; if (!settings.SchemaOnly) { continue; } } } Guid columnId = Guid.NewGuid(); SelectColumn selectColumn = new SelectColumn { IsHidden = orderedReportColumn.ColumnIsHidden ?? false, DisplayName = orderedReportColumn.Name, Expression = scalarExpression, ColumnId = columnId, EntityId = orderedReportColumn.Id }; context.ReportColumnMap[orderedReportColumn.Id] = columnId; columns.Add(selectColumn); } return(columns); }
/// <summary> /// Constructor /// </summary> internal CachingReportToQueryConverterKey(Report report, ReportToQueryConverterSettings settings) { ReportId = report.Id; ConditionsOnly = settings.ConditionsOnly; _hashCode = GenerateHashCode(); }