Exemplo n.º 1
0
        /// <summary>
        /// Unlike read write is called exactly once per each row and not called at all if the row's state does not require actual writing.
        /// </summary>
        /// <param name="execContext"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        protected override object Write(IDataLoaderWriteContext execContext)   //IDataLoaderContext execContext, Configuration configuration) {
        {
            Node node = execContext.CurrentNode;

            // Statement is already selected for the requested operation (While fetching the Configuration
            if (!string.IsNullOrWhiteSpace(Action(execContext).Query))
            {
                IADOTransactionScope scopedContext = execContext.OwnContextScoped as IADOTransactionScope;
                // Check if it is valid
                if (scopedContext == null)
                {
                    throw new NullReferenceException("Scoped synchronization and transaction context is not available.");
                }
                // Settings should be passed to the scopedContext in the ExternalServiceImp
                DbConnection  conn  = scopedContext.Connection;
                DbTransaction trans = scopedContext.StartADOTransaction();

                using (DbCommand cmd = conn.CreateCommand()) {
                    cmd.Transaction = trans;
                    cmd.Parameters.Clear();
                    ProcessCommand(cmd, Action(execContext).Query, execContext);

                    using (DbDataReader reader = cmd.ExecuteReader()) {
                        do
                        {
                            while (reader.Read())
                            {
                                for (int i = 0; i < reader.FieldCount; i++)
                                {
                                    string fname = reader.GetName(i);
                                    if (fname == null)
                                    {
                                        continue;
                                    }
                                    fname = fname.ToLower().Trim();
                                    // fname = fname.Trim(); // TODO: We have to rethink this - lowercasing seems more inconvenience than a viable protection against human mistakes.
                                    if (fname.Length == 0)
                                    {
                                        throw new Exception("Empty field name in a store context in nodedesfinition: " + node.NodeSet.Name);
                                    }
                                    object v = reader.GetValue(i);
                                    execContext.Row[fname] = (v is DBNull) ? null : v;
                                }
                            }
                            // This is important, we have been doing this for a single result before, but it is better to assume more than one, so that
                            // update of the data being written can be done more freely - using more than one select statement after writing. This is
                            // probably rare, but having the opportunity is better than not having it.
                        } while (reader.NextResult());
                        if (execContext.Operation != OPERATION_DELETE)
                        {
                            execContext.DataState.SetUnchanged(execContext.Row);
                        }
                    }
                }
            }
            return(null); // if this is not null it should add new results in the data
            // TODO: Consider if this is possible and useful (for some future version - not urgent).
        }
Exemplo n.º 2
0
        protected virtual Dictionary <string, object> LoadLookups(
            LoadedNodeSet loaderContext,
            IProcessingContext processingContext,
            IPluginServiceManager pluginServiceManager,
            IPluginsSynchronizeContextScoped contextScoped,
            INode currentNode)
        {
            Dictionary <string, object>         returnResult  = new Dictionary <string, object>();
            List <Dictionary <string, object> > results       = null;
            Dictionary <string, object>         currentResult = null;

            // This shouldn't happen to a dog! but I needed a hack for basket consumer.
            LookupLoaderContext.Instance.PluginServiceManager = pluginServiceManager;
            LookupLoaderContext.Instance.LoaderContext        = loaderContext;
            LookupLoaderContext.Instance.ProcessingContext    = processingContext;
            //

            Lookup lookup = (Lookup)currentNode;

            if (lookup.HasStatement())
            {
                results = new List <Dictionary <string, object> >();

                IADOTransactionScope scopedContext = contextScoped as IADOTransactionScope;
                // Check it is valid

                if (scopedContext == null)
                {
                    throw new NullReferenceException("Scoped synchronization and transaction context is not available.");
                }

                DbConnection conn = scopedContext.Connection;
                using (DbCommand cmd = conn.CreateCommand())
                {
                    cmd.CommandText = lookup.Query;
                    using (DbDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            while (reader.Read())
                            {
                                currentResult = new Dictionary <string, object>(reader.FieldCount);
                                currentResult = new Dictionary <string, object>(reader.FieldCount);
                                for (int i = 0; i < reader.FieldCount; i++)
                                {
                                    string fldname = reader.GetName(i);
                                    if (fldname == null)
                                    {
                                        continue;
                                    }
                                    fldname = fldname.ToLower().Trim();
                                    if (fldname.Length == 0)
                                    {
                                        throw new Exception($"Empty name when reading the output of a query. The field index is {i}. The query is: {cmd.CommandText}");
                                    }
                                    if (currentResult.ContainsKey(fldname))
                                    {
                                        throw new Exception($"Duplicated field name in the output of a query. The field is:{fldname}, the query is: {cmd.CommandText}");
                                    }
                                    object o = reader.GetValue(i);
                                    currentResult.Add(fldname, (o is DBNull) ? null : o);
                                }
                                results.Add(currentResult);
                            }
                        }
                    }
                }
                returnResult.Add(lookup.BindingKey, results);
            }
            return(returnResult);
        }
Exemplo n.º 3
0
        /*  Considerations:
         *      We have to read the configuration in a single turn and consume it from the internal configuration container
         *      in order to avoid direct hard coded dependency on the system configuration structure - most notably dependency on its
         *      structure and interpreatation.
         *  Solution:
         *      We have a nested class in which the configuration values used by this loader are collected (sometimes with some preprocessing)
         *  Lifecycle:
         *      The configuration (part of it at least) has a lifecycle equal to the node execution which is shorter than the life of the dataloader (at least potentially),
         *      so the configuration has to be reread on each node executio and thus is not persisted in the loader's instance, but passed to the main overridable methods.
         *
         */
        //protected class Configuration {
        //    public string Statement { get; set; }

        //}
        /// <summary>
        ///
        /// </summary>
        /// <param name="execContext"></param>
        /// <returns></returns>
        //protected virtual Configuration ReadConfiguration(IDataLoaderContext execContext) {
        //    var cfg = new Configuration();

        //    if (execContext.Action == ACTION_READ) {
        //        cfg.Statement = execContext.CurrentNode.Read.Select.HasStatement?execContext.CurrentNode.Read.Select.Query:null;
        //    } else if (execContext.Action == ACTION_WRITE) {
        //        switch (execContext.Operation) {
        //            case OPERATION_INSERT:
        //                if (execContext.CurrentNode.Write.Insert.HasStatement) {
        //                    cfg.Statement = execContext.CurrentNode.Write.Insert.Query;
        //                }
        //                break;
        //            case OPERATION_UPDATE:
        //                if (execContext.CurrentNode.Write.Update.HasStatement) {
        //                    cfg.Statement = execContext.CurrentNode.Write.Update.Query;
        //                }
        //                break;
        //            case OPERATION_DELETE:
        //                if (execContext.CurrentNode.Write.Delete.HasStatement) {
        //                    cfg.Statement = execContext.CurrentNode.Write.Delete.Query;
        //                }
        //                break;
        //        }
        //    }
        //    return cfg; ///////////////////////////////
        //}
        #endregion


        //public async Task<object> ExecuteAsync(IDataLoaderContext execContext) {
        //    // The procedure is different enough to deserve splitting by read/write
        //    Configuration cfg = ReadConfiguration(execContext);
        //    object result;
        //    if (execContext.Action == ACTION_WRITE) {
        //        result = ExecuteWrite(execContext, cfg);
        //    } else if (execContext.Action == ACTION_READ) {
        //        result = ExecuteRead(execContext, cfg);
        //    } else {
        //        // unknown action
        //        throw new Exception("Unknown action (only read/write) are supported");
        //    }
        //    return await Task.FromResult(result);
        //}

        protected override List <Dictionary <string, object> > Read(IDataLoaderReadContext execContext)
        {
            // TODO: What to return if there is no statement:
            //  I think we should have two policies - empty object which enables children extraction if logically possible and
            //  null wich stops the processing here.
            List <Dictionary <string, object> > results       = new List <Dictionary <string, object> >();
            Dictionary <string, object>         currentResult = null;
            Node node = execContext.CurrentNode;

            if (!string.IsNullOrWhiteSpace(Action(execContext).Query))
            {
                // Scope context for the same loader
                IADOTransactionScope scopedContext = execContext.OwnContextScoped as IADOTransactionScope;
                // Check it is valid
                if (scopedContext == null)
                {
                    throw new NullReferenceException("Scoped synchronization and transaction context is not available.");
                }
                // Configuration settings Should be set to the scoped context during its creation/obtainment - see ExternalServiceImp

                // No tranaction in read mode - lets not forget that closing the transaction also closes the connection - so the ;ifecycle control will do this using the transaction based notation
                // from ITransactionScope
                DbConnection conn = scopedContext.Connection;
                using (DbCommand cmd = conn.CreateCommand()) {
                    cmd.Transaction = scopedContext.CurrentTransaction; // if we decide to open transaction in future this will guarantee we only have to open it and will take effect throughout the code.
                    cmd.Parameters.Clear();
                    // This will set the resulting command text if everything is Ok.
                    // The processing will make replacements in the SQL and bind parameters by requesting them from the resolver expressions configured on this node.
                    // TODO: Some try...catching is necessary.
                    ProcessCommand(cmd, Action(execContext).Query, execContext);
                    using (DbDataReader reader = cmd.ExecuteReader()) {
                        do
                        {
                            if (reader.HasRows)
                            {
                                // Read a result (many may be contained) row by row
                                while (reader.Read())
                                {
                                    currentResult = new Dictionary <string, object>(reader.FieldCount);
                                    for (int i = 0; i < reader.FieldCount; i++)
                                    {
                                        string fldname = reader.GetName(i);
                                        if (fldname == null)
                                        {
                                            continue;
                                        }
                                        // TODO: May be configure that or at least create a compile time definition
                                        fldname = fldname.ToLower().Trim(); // TODO: lowercase
                                        //fldname = fldname.Trim();
                                        if (fldname.Length == 0)
                                        {
                                            throw new Exception($"Empty name when reading the output of a query. The field index is {i}. The query is: {cmd.CommandText}");
                                        }
                                        if (currentResult.ContainsKey(fldname))
                                        {
                                            throw new Exception($"Duplicated field name in the output of a query. The field is:{fldname}, the query is: {cmd.CommandText}");
                                        }
                                        object v = reader.GetValue(i);
                                        currentResult.Add(fldname, (v is DBNull) ? null : v);
                                    }
                                    // Mark the records unchanged, because they are just picked up from the data store (rdbms in this case).
                                    execContext.DataState.SetUnchanged(currentResult);
                                    results.Add(currentResult);
                                    if (!node.IsList)
                                    {
                                        break;
                                    }
                                }
                            }
                        } while (reader.NextResult());
                    }
                }
            }
            return(results); // TODO: Decide what behavior we want with empty statements. I for one prefer null result, effectively stopping the operation.
        }