/// <summary> /// Reads data using DDE. This is extremely show and should only ever be used as a last resort. /// </summary> /// <param name="mocktables"></param> internal void GetDataByDDE(List <TableDef> mocktables) // needs fixing { /* DDE requests are limited to a maximum length of 255 characters, * which is easily exceeded. A workaround is splitting the requests. * Not pretty but the only way to get to many-many relationships that contain >93750 worth of connected characters * without setting the maxfieldsize higher. */ List <List <CommenceValue> > rows; List <CommenceValue> rowvalues; ICommenceDatabase db = new CommenceDatabase(); // always define a category db.ViewCategory(this.cursor.Category); // are we dealing with a view? if (!string.IsNullOrEmpty(cursor.View)) { db.ViewView(this.cursor.View); } int itemCount = db.ViewItemCount(); for (int i = 1; i <= itemCount; i++) // note that we use a 1-based iterator { rows = new List <List <CommenceValue> >(); rowvalues = new List <CommenceValue>(); foreach (TableDef td in mocktables) { string[] DDEResult = null; List <string> fieldNames = td.ColumnDefinitions.Select(o => o.FieldName).ToList(); if (td.Primary) { // ViewFields and ViewConnectedFields have a limited capacity // the total length of a DDE command cannot exceed 255 characters // What we are going to do is limit the number of characters to a value of up to 150 chars, // to be on the safe side (ViewConnectedFilter and two delimiters already make up 35 characters!) ListChopper lcu = new ListChopper(fieldNames, 150); foreach (List <string> l in lcu.Portions) { DDEResult = db.ViewFields(i, l); // we have our results, we now have to create CommenceValue objects from it // and we also have to match them up with their respective column // this is a little tricky... for (int j = 0; j < DDEResult.Length; j++) { ColumnDefinition cd = td.ColumnDefinitions.Find(o => o.FieldName.Equals(l[j])); string[] buffer = new string[] { DDEResult[j] }; //buffer = FormatValues(buffer,this.Formatting, cd); buffer = FormatValues(buffer, cd); CommenceValue v = new CommenceValue(buffer[0], cd); rowvalues.Add(v); } // for } // list l } else // we are dealing with a connection { int conItemCount = db.ViewConnectedCount(i, td.ColumnDefinitions[0].Connection, td.ColumnDefinitions[0].Category); // doesn't matter which one we use // here's a nice challenge: // upon every iteration we get a row of fieldvalues from the connection // to make things worse, we chop them up so it aren't even complete rows. // we must aggregate the values for each field. // We'll construct a datatable to hack around that; // we could have also used a dictionary I suppose. // using a datatable may be easiest DataTable dt = new DataTable(); for (int c = 0; c < fieldNames.Count; c++) { dt.Columns.Add(fieldNames[c]); // add fields as columns, keeping everything default } // loop all connected items for (int citemcount = 1; citemcount <= conItemCount; citemcount++) { DataRow dr = dt.NewRow(); // create a row containing all columns ListChopper lcu = new ListChopper(fieldNames, 150); foreach (List <string> list in lcu.Portions) { DDEResult = db.ViewConnectedFields(i, td.ColumnDefinitions[0].Connection, td.ColumnDefinitions[0].Category, citemcount, list); // populate colums for the fields we requested for (int j = 0; j < DDEResult.Length; j++) { dr[list[j]] = DDEResult[j]; } } // list l dt.Rows.Add(dr); } // citemcount // create a CommenceValue from every column in the datatable foreach (DataColumn dc in dt.Columns) { // this will also return columns that have no data, which is what we want. string[] query = (from r in dt.AsEnumerable() select r.Field <String>(dc.ColumnName)).ToArray(); ColumnDefinition cd = td.ColumnDefinitions.Find(o => o.FieldName.Equals(dc.ColumnName)); CommenceValue cv = null; if (query.Length > 0) // only create value if there is one { //query = FormatValues(query, this.Formatting, cd); query = FormatValues(query, cd); cv = new CommenceValue(query, cd); } else { // create empty CommenceValue cv = new CommenceValue(cd); } rowvalues.Add(cv); } } // if } // foreach tabledef rows.Add(rowvalues); CursorDataReadProgressChangedArgs args = new CursorDataReadProgressChangedArgs(rows, i, totalRows); // progress within the cursor OnDataProgressChanged(args); } // i db = null; ExportCompleteArgs a = new ExportCompleteArgs(itemCount); OnDataReadCompleted(a); }
protected override void ProcessRecord() { var db = new CommenceDatabase(); string result = db.ViewCategory(fromCategory); // if fromCategory is not found, nothing happens if (result.ToLower() != "ok") { WriteError(new ErrorRecord(new Vovin.CmcLibNet.CommenceDDEException(result), "CategoryNotFound", ErrorCategory.InvalidResult, db)); return; } string clarifyState = db.ClarifyItemNames(); try { int connectedItemCount = -1; // no item supplied, process entire category if (string.IsNullOrEmpty(fromItem)) { int numItems = db.GetItemCount(fromCategory); List <string> itemNames = db.GetItemNames(fromCategory); db.ClarifyItemNames("TRUE"); for (int i = 1; i <= numItems; i++) // DDE-call indexes in Commence are 1-based { // since this not rely on string values, it should be reliable connectedItemCount = db.ViewConnectedCount(i, connectionName, toCategory); WriteObject(new { ItemName = itemNames[i - 1], FromCategory = fromCategory, Connection = connectionName, ToCategory = toCategory, Count = connectedItemCount }, false); // return and do not enumerate. I.e. pass every object separately. } } else { if (!string.IsNullOrEmpty(clarifyValue) && !string.IsNullOrEmpty(clarifySeparator)) { fromItem = db.GetClarifiedItemName(fromItem, clarifySeparator, clarifyValue); db.ClarifyItemNames("TRUE"); } connectedItemCount = db.GetConnectedItemCount(fromCategory, fromItem, connectionName, toCategory); WriteObject(new { ItemName = fromItem, FromCategory = fromCategory, Connection = connectionName, ToCategory = toCategory, Count = connectedItemCount }, false); } if (connectedItemCount == -1) { WriteError(new ErrorRecord(new Vovin.CmcLibNet.CommenceDDEException("Vovin.CmcLibNet was unable to get a valid result from Commence."), "CommenceDDEError", ErrorCategory.InvalidArgument, db)); WriteVerbose("A count of -1 means an error occurred while trying to receive the count from Commence.\n" + "This is likely caused by one or more of the arguments being invalid.\n" + "Note that connection- and viewnames in Commence are case-sensitive!\n\n" + "A clarify separator may include spaces. Make sure to include them in the command.\n\n"); return; } } finally { db.ClarifyItemNames(clarifyState); // restore state db.Close(); } }