title: How to make Parallel.ForEach split work into larger chunks?
body: <p>I'm using <code>Parallel.ForEach</code> to perform some work on Oracle DB, and I see from the log that it doesn't split the load into N chunks where N = <code>MaxDegreeOfParallelism</code>. This is expected, since each chunk can take longer or shorter to process, but the chunks is does split the workload into are too small. ODP.NET would for some reason overload the DB with open connections if pooling was on (ORA-12518), so I disabled it. This got rid of the errors, but I would like to cut down on the time spent opening and closing connections.</p>
  
  <p>Is there a way to influence <code>Parallel.ForEach</code> to split the workload into larger chunks?</p>
  
  <p>For reference, this is what the code currently looks like:</p>
  
  <pre><code>//conn is the primary connection stored in the object
  
  logger.Report("Started");
  
  var allObjects = GetObjects(); //uses the primary connection
  logger.Report(string.Format("Retrieved {0} objects", allObjects.Count));
  
  var i = allObjects.Count;
  var taskID = 0;
  
  Parallel.ForEach(
      allObjects,
      new ParallelOptions { MaxDegreeOfParallelism = 16,  },
      () =&gt; {
          var c = (DbConnection)((ICloneable)conn).Clone();
          c.Open();
          var t = Interlocked.Increment(ref taskID);
          logger.Report(string.Format("Task #{0} started", t));
          return (conn: c, task: t);
      },
      (o, loopState, c) =&gt; {
          try {
              var objectName = o.Name;
              var objectType = o.Type;
  
              logger.Report(string.Format("Retrieving {0} {1}", objectType, objectName));
              var dbObject = GetObject(c.conn, o.Name, o.Type);
  
              logger.Report(string.Format("Processing {0} {1}", objectType, objectName));
              var result = ProcessObject(dbObject);
  
              logger.Report(string.Format("Recompiling {0} {1}", objectType, objectName));
              ProcessResult(c.conn, result);
  
              logger.Report(string.Format("{0} objects remaining", Interlocked.Decrement(ref i)));
  
              return c;
          } catch (Exception ex) {
              logger.Report("ERROR: " + ex);
              throw;
          }
      },
      c =&gt; {
          logger.Report(string.Format("Task #{0} finished", c.task));
          c.conn.Close();
          c.conn.Dispose();
      });
  
  logger.Report("Recompiling invalids...");
  RecompileInvalids(); //uses the primary connection
  
  logger.Report("Done");
  </code></pre>