Exemple #1
0
        /// <summary>
        /// Executes a read action on a node. The procedure is:
        /// - Single and multiple results are processed the same way, but when single result is required (not is list) all but the first one are removed
        /// - For each result all child nodes are executed (i.e. this routine is executed on each result and they create/update a property corresponding
        ///     to the key under which they are configured. This is achieved by calling the function with the node argument that gives it the key under which it
        ///     should attach its result(s).
        /// </summary>
        /// <param name="node"></param>
        /// <param name="parentResult"></param>
        /// <param name="dataIteratorContext"></param>
        /// <returns></returns>
        private object ExecuteReadNode(Node node, IEnumerable <Dictionary <string, object> > parentResult, DataIteratorContext dataIteratorContext)
        {
            #region Preparation of necessary structures
            object returnResult = null;
            // We have to iterate through the parent rows, so we cannot create the results here, but we will do it as soon as possible
            List <Dictionary <string, object> > results = null;

            NodeExecutionContext.Manager execContextManager = new NodeExecutionContext.Manager(dataIteratorContext, node, ACTION_READ);
            #endregion

            // MAIN CYCLE - repeated once per each parent result (for the starting node - it is repeated once)

            foreach (Dictionary <string, object> row in parentResult)
            {
                using (var stackframe = execContextManager.Datastack.Scope(row))
                {
                    // 1. Load the main plugin - Data Loader kind
                    IDataLoaderPlugin dataPlugin = null;
                    IPluginsSynchronizeContextScoped contextScoped = null;
                    if (!string.IsNullOrEmpty(node.DataPluginName))
                    {
                        dataPlugin = dataIteratorContext?.DataLoaderPluginAccessor.LoadPlugin(node.DataPluginName);
                        // 2. Update the Node execution context with the actual data for the current loop.
                        //Dictionary<string, object> parentResult = null;
                        contextScoped = dataIteratorContext.DataLoaderPluginAccessor.GetPluginsSynchronizeContextScoped(node.DataPluginName, dataPlugin).Result;
                        if (contextScoped is IContextualBasketConsumer)
                        {
                            var consumer = contextScoped as IContextualBasketConsumer;
                            consumer.InspectBasket(new NodeContextualBasket(execContextManager));
                        }
                    }

                    execContextManager.DataLoaderContextScoped = contextScoped;
                    // execContextManager.ParentResult = row; // Wrong
                    //execContextManager.Phase = "BEFORE_SQL"; // I think 'Phase' is a relic, couldn't find any usage.
                    execContextManager.Row = row; // This is needed by the resolvers, but is not visible to plugins!

                    // 2.1 The reulsts - from this part on we need it
                    results = new List <Dictionary <string, object> >();
                    // 2.1.1 Put it into the execution context so everything executed from this point on can put results into the collection
                    execContextManager.Results = results;

                    // 3. Execute custom plugins (injects) before main work
                    // 3.1. Prepare plugins - through the processor
                    // object customPluginsResults = new Dictionary<string, object>();
                    ICustomPluginProcessor plugins = new CustomPluginProcessor();

                    // 3.3 The procedure has been changed, now the plugins are responsible to attach results in the Results collection
                    #region 3.3 execute BEFORE SQL plugins over the results
                    if (node.Read != null)
                    {
                        plugins.Execute(node.Read.BeforeNodeActionPlugins, execContextManager.CustomPluginProxy);
                    }
                    #endregion

                    #region (DEPRECATED CODE) Execute BeforeSQL plugins
                    //pluginExecuteParameters = new CustomPluginExecuteParameters
                    //{
                    //    Phase = "BEFORE_SQL",
                    //    Row = row,
                    //    Results = null,
                    //    Parents = dataIteratorContext.Datastack,
                    //    Path = node.NodeKey, // TODO: This must be the full path to this node.
                    //    Action = OPERATION_READ,
                    //    NodeKey = node.NodeKey,
                    //    NodeParameters = parametersContext.PublicContext

                    //};
                    //pluginExecuteParameters.SqlStatement = GetSqlStatement(node, ACTION_SELECT);
                    //ICustomPluginContext customPluginContext
                    //    = new CustomPluginContext(null, dataIteratorContext.ProcessingContext, contextScoped, node, dataIteratorContext.PluginServiceManager, dataIteratorContext.CustomService);
                    //ICustomPluginProcessor plugins = new CustomPluginProcessor(customPluginContext);

                    //customPluginsResults = plugins?.ExecuteAsync(node.BeforeDataExecutionPlugins, pluginExecuteParameters).Result;
                    #endregion

                    // 4. Data loader execution
                    // 4.1. Execute it (we alreade got the plugin in the beginning - see 1.)
                    // 4.1.1. Update context
                    //execContextManager.Phase = "SQL";
                    // 4.1.2. Execute loader
                    if (execContextManager.LoaderContext.StartNode.Read != null && dataPlugin != null)
                    {
                        dataPlugin.Execute(execContextManager.LoaderPluginProxy);
                    }
                    // The data  loader plugins are responsible to put their results into the REsults collection in the execution context

                    #region DEPRECATED CODE - see above
                    // 4.2. Analyze the result and put it in the official results
                    //if (nodeResult != null && nodeResult is IDictionary) // In order to count as element (record - single record)
                    //{
                    //    results = new List<Dictionary<string, object>>() { ReDictionary(nodeResult) };
                    //}
                    //else if (nodeResult != null && nodeResult is IEnumerable)
                    //{
                    //    // Anything enumerable that is not a dictionary itself we treat as a list/set of results
                    //    //  which shuld be each a IDictionary at least - in order to represent an item.
                    //    results = new List<Dictionary<string, object>>();
                    //    foreach (object el in (nodeResult as IEnumerable))
                    //    {
                    //        if (el is IDictionary)
                    //        {
                    //            // TODO: What if some of the elements is null? Is it a failure or just an empty entry?
                    //            results.Add(ReDictionary(el));
                    //        }
                    //        else
                    //        {
                    //            throw new Exception("The returned result set cotains non-dictionary elements.");
                    //        }
                    //    }
                    //}
                    //else
                    //{
                    //    throw new ArgumentException($"The return result from a dataplugin has to be either IDictionary<string, object> or List<Dictionary<string, object>> (additional info: node - {node.DataPluginName}");
                    //}
                    #endregion

                    #region DEPRECATED CODE - As with the data loaders the plugins should put their results in the Results themselves
                    // 4.3. Combine the data loader results with those from the custom plugins (if any)
                    // TODO: Probably we should prepend this???
                    // TODO: Use more relaxed type requirements for the results of the plugin (may be?).
                    //if (customPluginsResults != null && customPluginsResults is IEnumerable<Dictionary<string, object>>)
                    //{
                    //    if (results == null)
                    //    {
                    //        results = new List<Dictionary<string, object>>();
                    //    }
                    //    results.AddRange(customPluginsResults as IEnumerable<Dictionary<string, object>>);
                    //}
                    #endregion

                    #region DEPRECATED CODE DataLoaderImp

                    //object nodeResult = dataPlugin?
                    //    .ExecuteAsync(execContextManager.Context)
                    //    .Result;
                    //// Strict type checking is dangerous - when I saw it first this received REadOnlyDictionary which is not equal to Dictionary.
                    //// if (nodeResult?.GetType() == typeof(Dictionary<string, object>))
                    //if (nodeResult != null && nodeResult is IDictionary) // In order to count as element (record - single record)
                    //{
                    //    results = new List<Dictionary<string, object>>() { ReDictionary(nodeResult) };
                    //    ////results.Add((nodeResult as IDictionary))
                    //    // results.Add(nodeResult as Dictionary<string, object>);
                    //}
                    //// else if (nodeResult?.GetType() == typeof(List<Dictionary<string, object>>))
                    //else if (nodeResult != null && nodeResult is IEnumerable)
                    //{
                    //    results = new List<Dictionary<string, object>>();
                    //    foreach (object el in (nodeResult as IEnumerable))
                    //    {
                    //        if (el is IDictionary)
                    //        {
                    //            // What if some of the elements is null? Is it a failure or just an empty entry?
                    //            results.Add(ReDictionary(el));
                    //        }
                    //        else
                    //        {
                    //            throw new Exception("The returned result set cotains non-dictionary elements.");
                    //        }
                    //    }
                    //}
                    //else
                    //{
                    //    throw new ArgumentException($"The return result from a dataplugin has to be either IDictionary<string, object> or List<Dictionary<string, object>> (additional info: node - {node.DataPluginName}");
                    //}
                    //// Probably we should prepend this.
                    //// TODO: Use more relaxed type requirements for the results of the plugin (may be?).
                    //if (customPluginsResults != null && customPluginsResults is IEnumerable<Dictionary<string, object>>)
                    //    results.AddRange(customPluginsResults as IEnumerable<Dictionary<string, object>>);
                    #endregion

                    // We have to discuss this - once comitted there is no going back, so we have to be sure this is the best behavior!
                    // 5. Execute custom plugins after SQL
                    // Update context (TODO: are there any more updates neccessary?)
                    //execContextManager.Phase = "AFTER_SQL";
                    // execContextManager.Results = results; // NO NEED
                    // 5.1. After data load (traditionally called AFTER SQL) custom plugins execution.
                    // Now the plugins are required to write to the REsults of the execution context themselves.
                    if (node.Read != null)
                    {
                        plugins?.Execute(node.Read.AfterNodeActionPlugins, execContextManager.CustomPluginProxy);
                    }

                    #region DEPRECATED CODE - see above
                    // 5.2. Accomodate the custom plugin results into the results
                    //if (customPluginsResults != null && customPluginsResults is IEnumerable<Dictionary<string, object>>)
                    //{
                    //    if (results == null)
                    //    {
                    //        results = new List<Dictionary<string, object>>();
                    //    }
                    //    results.AddRange(customPluginsResults as IEnumerable<Dictionary<string, object>>);
                    //}
                    #endregion

                    #region AfterSQL Plugins (Deprecated code)
                    //pluginExecuteParameters = new CustomPluginExecuteParameters
                    //{
                    //    Phase = "AFTER_SQL",
                    //    Row = row,
                    //    Results = null,
                    //    Parents = dataIteratorContext.Datastack,
                    //    Path = node.NodeKey,
                    //    NodeParameters = parametersContext.PublicContext,
                    //    Action = OPERATION_READ,
                    //    NodeKey = node.NodeKey
                    //};
                    //pluginExecuteParameters.SqlStatement = GetSqlStatement(node, ACTION_SELECT);

                    //customPluginsResults = plugins?.ExecuteAsync(node.AfterDataExecutionPlugins, pluginExecuteParameters).Result;

                    //if (customPluginsResults != null && customPluginsResults is IEnumerable<Dictionary<string, object>>)
                    //    results.AddRange(customPluginsResults as IEnumerable<Dictionary<string, object>>);
                    #endregion

                    // 6. Execute the child nodes
                    foreach (Node childNode in node.Children.OrderBy(n => n.ExecutionOrder))
                    {
                        ExecuteReadNode(childNode, results, dataIteratorContext);
                    }

                    #region Child Nodes (Deprecated code)

                    //if (node.Children != null && node.Children.Count > 0)
                    //{
                    //    IterateChildren(node, results, dataIteratorContext);
                    //}

                    #endregion

                    // 7. Execute custom plugins after children
                    // Update context (TODO: are there any more updates?)
                    //execContextManager.Phase = "AFTER_CHILDREN";
                    // execContextManager.Results = results; // NO NEED - the reference is already there
                    // 7.1. - execute plugin
                    if (node.Read != null)
                    {
                        plugins?.Execute(node.Read.AfterNodeChildrenPlugins, execContextManager.CustomPluginProxy);
                    }
                    // 7.2. Accomodate the custom plugin results into the results
                    #region DEPRECATED CODE
                    //if (customPluginsResults != null && customPluginsResults is IEnumerable<Dictionary<string, object>>)
                    //{
                    //    if (results == null) results = new List<Dictionary<string, object>>();
                    //    results.AddRange(customPluginsResults as IEnumerable<Dictionary<string, object>>);
                    //}
                    #endregion

                    #region AfterChildren Plugins (Deprecated code)
                    //pluginExecuteParameters = new CustomPluginExecuteParameters
                    //{
                    //    Phase = "AFTER_CHILDREN",
                    //    Row = row,
                    //    Results = null,
                    //    Parents = dataIteratorContext.Datastack,
                    //    Path = node.NodeKey,
                    //    NodeParameters = parametersContext.PublicContext,
                    //    Action = OPERATION_READ,
                    //    NodeKey = node.NodeKey
                    //};
                    //pluginExecuteParameters.SqlStatement = GetSqlStatement(node, ACTION_SELECT);

                    //customPluginsResults = plugins?.ExecuteAsync(node.AfterChildreDataExecutionPlugins, pluginExecuteParameters).Result;

                    //if (customPluginsResults != null && customPluginsResults is IEnumerable<Dictionary<string, object>>)
                    //    results.AddRange(customPluginsResults as IEnumerable<Dictionary<string, object>>);
                    #endregion
                }
                // 8. Final repacking
                #region Final Re-Packing
                if (node.IsList)
                {
                    row.Add(node.NodeKey.Trim(), results);
                    returnResult = results;
                }
                else if (results != null && results.Count > 0)
                {
                    row.Add(node.NodeKey.Trim(), results[0]);
                    returnResult = results[0];
                }
                #endregion
            }

            return(returnResult);
        }
Exemple #2
0
        private object ExecuteWriteNode(Node node, object dataNode, string nodePath, DataIteratorContext dataIteratorContext)
        {
            #region Preparation of necessary structures
            List <Dictionary <string, object> > currentNode = null;
            object result = dataNode;

            NodeExecutionContext.Manager execContextManager = new NodeExecutionContext.Manager(dataIteratorContext, node, ACTION_WRITE);
            // TODO: ?? execContextManager.Data = dataNode;
            #endregion

            #region Deprecated code
            //CustomPluginExecuteParameters pluginExecuteParameters;

            //ParameterResolverContext parametersContext = new ParameterResolverContext(node)
            //{
            //    ExternalService = dataIteratorContext.ExternalService,
            //    CustomService = dataIteratorContext.CustomService,
            //    PluginServiceManager = dataIteratorContext.PluginServiceManager,
            //    LoaderContext = dataIteratorContext.LoaderContext,
            //    ProcessingContext = dataIteratorContext.ProcessingContext,
            //    Datastack = dataIteratorContext.Datastack,
            //    OverrideAction = dataIteratorContext.OverrideAction
            //};

            //NodeExecutionContext.Manager execContextManager = new NodeExecutionContext.Manager(
            //    loaderContext: dataIteratorContext.LoaderContext,
            //    parentResult: null, // Determined only while iterating - the parent result of the individual iteration
            //    pluginServiceManager: dataIteratorContext.PluginServiceManager,
            //    contextScoped: null, // One is obtained for each iteration
            //    currentNode: node,
            //    processingContext: dataIteratorContext.ProcessingContext,
            //    customService: dataIteratorContext.CustomService
            //);

            //object injectResults;
            #endregion

            // 1. Extract the data from more generic forms to list<dictionary> form
            currentNode = ReCodeDataNode(dataNode);
            #region Deprecated code
            //ExtractDataNode(dataNode, ref currentNode, ref result);
            #endregion

            // 1.1. Load the data loader plugin
            IDataLoaderPlugin dataPlugin = null;
            IPluginsSynchronizeContextScoped contextScoped = null;
            if (!string.IsNullOrEmpty(node.DataPluginName))
            {
                dataPlugin = dataIteratorContext?.DataLoaderPluginAccessor.LoadPlugin(node.DataPluginName);

                //Dictionary<string, object> parentResult = null;
                contextScoped = dataIteratorContext.DataLoaderPluginAccessor.GetPluginsSynchronizeContextScoped(node.DataPluginName, dataPlugin).Result;
                if (contextScoped is IContextualBasketConsumer)
                {
                    var consumer = contextScoped as IContextualBasketConsumer;
                    consumer.InspectBasket(new NodeContextualBasket(execContextManager));
                }
            }

            // 2. Main cycle.
            //  Split by ordering the items by non-deleted and deleted state for easier processing
            foreach (Dictionary <string, object> row in
                     currentNode.OrderBy(n => (n.ContainsKey(STATE_PROPERTY_NAME) &&
                                               (string)n[STATE_PROPERTY_NAME] == STATE_PROPERTY_DELETE
                                               ) ? 0
                      : 1))
            {
                //TODO the current implementation is not enforcing the IsList property!!!
                if (row == null)
                {
                    continue;
                }

                // 3 Get the state
                var state = execContextManager.DataState.GetDataState(row); //row[STATE_PROPERTY_NAME] as string;
                // 3.1 Check for valid state
                if (state == null)                                          // Deprecated:  row.ContainsKey(STATE_PROPERTY_NAME) == false)
                {
                    continue;                                               // Skip non-existent or invalid state - this also skips the children of this node!
                    // throw new ArgumentException("{0} property missing from row. Row processing skipped in ExecuteWriteNode", STATE_PROPERTY_NAME);
                }
                // 3.2 Determine the actual state to use for this iteration (we may have override)
                string operation = GetWriteAction(execContextManager.OverrideAction, state);

                // The action is the actual state we assume!

                #region Fill the values for current iteration in the exec context
                execContextManager.DataLoaderContextScoped = contextScoped; // Change it for this iteration
                // execContextManager.ParentResult = row; // Wrong
                //execContextManager.Phase = "BEFORE_SQL";
                execContextManager.Row       = row;
                execContextManager.Operation = operation;
                #endregion

                // 5. Execute plugins before SQL
                // DEPRECATED :Results from plugins - we will reuse this in the next phases too.
                // Now the plugins have to store data themselves in Row
                // object customPluginsResults = null;
                ICustomPluginProcessor plugins = new CustomPluginProcessor();

                // 5.1. Do execute
                if (node.Write != null)
                {
                    // customPluginsResults =
                    plugins?.Execute(node.Write.BeforeNodeActionPlugins, execContextManager.CustomPluginProxy);
                }
                #region DEPRECATED CODE Execute BeforeSQL plugins
                //pluginExecuteParameters = new CustomPluginExecuteParameters
                //{
                //    Phase = "BEFORE_SQL",
                //    Row = row,
                //    Results = null,
                //    Parents = dataIteratorContext.Datastack,
                //    Path = node.NodeKey,
                //    NodeParameters = parametersContext.PublicContext,
                //    Action = OPERATION_WRITE,
                //    NodeKey = node.NodeKey
                //};
                //pluginExecuteParameters.SqlStatement = GetSqlStatement(node, action);
                //ICustomPluginContext customPluginContext
                //    = new CustomPluginContext(null, dataIteratorContext.ProcessingContext, contextScoped, node, dataIteratorContext.PluginServiceManager, dataIteratorContext.CustomService);
                //ICustomPluginProcessor plugins = new CustomPluginProcessor();

                //customPluginsResults = plugins?.ExecuteAsync(node.BeforeDataExecutionPlugins, pluginExecuteParameters).Result;

                #endregion

                // 6. Execute children now if the operation is delete
                // Should we remove these?
                #region Reversed Order Processing
                if (operation == OPERATION_DELETE)
                {
                    if (node.Children != null && node.Children.Count > 0)
                    {
                        using (var stackframe = dataIteratorContext.Datastack.Scope(row))
                        {
                            dataIteratorContext.OverrideAction.Push(OPERATION_DELETE);

                            if (node.Children != null && node.Children.Count > 0)
                            {
                                if (row != null)
                                {
                                    foreach (Node childNode in node.Children)
                                    {
                                        string currentNodePath = (!string.IsNullOrEmpty(nodePath))
                                                                     ? nodePath + "." + childNode.NodeKey.Trim()
                                                                     : childNode.NodeKey.Trim();


                                        if (row.ContainsKey(childNode.NodeKey.Trim()))
                                        {
                                            // Re-assign the data to the node in case it has to be replaced by the node
                                            //  Not sure we need this, but for now we keep the option open.
                                            // However we should remove the deleted node
                                            object currentDataNode = row[childNode.NodeKey.Trim()];
                                            row[childNode.NodeKey.Trim()] =
                                                ExecuteWriteNode(childNode, currentDataNode, currentNodePath, dataIteratorContext);
                                        }
                                    }
                                }
                            }

                            dataIteratorContext.OverrideAction.Pop();
                        }
                    }
                }
                #endregion

                // 7. Execute main action for this node (the data plugin)
                #region 7. DataLoader processing

                if (operation != OPERATION_UNCHANGED && dataPlugin != null)
                {
                    dataPlugin?.Execute(execContextManager.LoaderPluginProxy);
                }
                #region (DEPRECATED CODE)
                // 7.1. Accomodate the results from before sql plugins
                //if (customPluginsResults != null && customPluginsResults is IEnumerable<Dictionary<string, object>>)
                //{
                //    ApplyResultsToRow(row, customPluginsResults as IEnumerable<Dictionary<string, object>>);
                //}
                #endregion
                #endregion


                #region 8. AfterNodeAction Plugins
                // 8. Switch to after SQL mode (in fact it is also after children if we have delete operation, but we have to catch up step by step.
                //execContextManager.Phase = "AFTER_SQL";

                #region (Deprecated code)

                /*
                 * pluginExecuteParameters = new CustomPluginExecuteParameters
                 * {
                 *  Phase = "AFTER_SQL",
                 *  Row = row,
                 *  Results = null,
                 *  Parents = dataIteratorContext.Datastack,
                 *  Path = node.NodeKey,
                 *  NodeParameters = parametersContext.PublicContext,
                 *  Action = ACTION_WRITE,
                 *  NodeKey = node.NodeKey
                 * };
                 * pluginExecuteParameters.SqlStatement = GetSqlStatement(node, operation);
                 */
                #endregion

                #region 8.1. Execute the plugins (after sql)
                if (node.Write != null)
                {
                    plugins?.Execute(node.Write.AfterNodeActionPlugins, execContextManager.CustomPluginProxy);
                }
                #endregion

                #region (DEPRECATED code)
                // (Deprecated) 8.2. Accomodate the results if any - this is now responsibility of the plugin itself
                //if (customPluginsResults != null && customPluginsResults is IEnumerable<Dictionary<string, object>>)
                //{
                //    ApplyResultsToRow(row, customPluginsResults as IEnumerable<Dictionary<string, object>>);
                //}

                #endregion

                #region NormalOrderProcessing
                // 9. Execute the children now (for non-delete operations).
                // Keeping this code inline makes it a bit easier to follow the processing procedure
                if (operation != OPERATION_DELETE)
                {
                    using (var stackframe = dataIteratorContext.Datastack.Scope(row))
                    {
                        if (node.Children != null && node.Children.Count > 0)
                        {
                            if (row != null)
                            {
                                foreach (Node childNode in node.Children)
                                {
                                    string currentNodePath = (!string.IsNullOrEmpty(nodePath))
                                                                 ? nodePath + "." + childNode.NodeKey.Trim()
                                                                 : childNode.NodeKey.Trim();


                                    if (row.ContainsKey(childNode.NodeKey.Trim()))
                                    {
                                        object currentDataNode = row[childNode.NodeKey.Trim()];
                                        row[childNode.NodeKey.Trim()] =
                                            ExecuteWriteNode(childNode, currentDataNode, currentNodePath, dataIteratorContext);
                                    }
                                }
                            }
                        }
                    }
                }
                #endregion

                #endregion

                #region AfterNodeChildren Plugins
                // 10. Switch to after children mode
                //execContextManager.Phase = "AFTER_SQL";

                #region Deprecated code
                //pluginExecuteParameters = new CustomPluginExecuteParameters
                //{
                //    Phase = "AFTER_CHILDREN",
                //    Row = row,
                //    Results = null,
                //    Parents = dataIteratorContext.Datastack,
                //    Path = node.NodeKey,
                //    Action = ACTION_WRITE,

                //    NodeKey = node.NodeKey,
                //    NodeParameters = parametersContext.PublicContext
                //};
                //pluginExecuteParameters.SqlStatement = GetSqlStatement(node, operation);
                #endregion deprecated code
                // 11. Execute the plugins for after children
                if (node.Write != null)
                {
                    plugins?.Execute(node.Write.AfterNodeChildrenPlugins, execContextManager.CustomPluginProxy);
                }
                #region DEPRECATED CODE
                // 11.1. Accomodate the results from the plugins.
                //if (customPluginsResults != null && customPluginsResults is IEnumerable<Dictionary<string, object>>)
                //    ApplyResultsToRow(row, customPluginsResults as IEnumerable<Dictionary<string, object>>);
                #endregion
                #endregion
            }

            // 12. We have to clean up deleted rows completely
            #region Final Re-Packing
            for (int i = currentNode.Count - 1; i >= 0; i--)
            {
                Dictionary <string, object> row = currentNode[i];
                if (row.ContainsKey(STATE_PROPERTY_NAME) && (row[STATE_PROPERTY_NAME] as string) == STATE_PROPERTY_DELETE)
                {
                    currentNode.RemoveAt(i); // Remove deleted records
                }
            }
            #endregion
            // TODO: needs better implementation. This is temporary fix only
            // TODO: Do we really need this at all??? result is kind of empty at the moment
            // result = UpdateResults(currentNode, result);

            //return result;\
            // This depends on the re-assignment.
            if (dataNode is IDictionary <string, object> )
            {
                if (currentNode.Count > 0)
                {
                    return(currentNode[0]);
                }
                return(null); // TODO: May be empty object? Can we be here at all if anything was wrong?
            }
            else
            {
                return(currentNode);
            }
        }