I want to assign datatable in collection/array as below
object[] dtObj = {Datatable1,Datatable2};
and loop like this,
for(int i=0; i<dtObj.lengt; i++)
{
foreach(Datarow row in dtObj[i].Rows)
{
// logic here
}
}
This way I want to loop through the datable one by one. How can I achieve this?
You can achieve it by not declaring it as object[] but as DataTable[]. If it has to be an object[] for whatever reason, you have to cast it to DataTable in the loop:
foreach(Datarow row in ((DataTable)dtObj[i]).Rows)
{
// logic here
}
If you want to add other objects than DataTable to this object[], you can use OfType:
foreach(DataTable table in dtObj.OfType<DataTable>())
{
foreach(DataRow row in table.Rows)
{
}
}
Related
I have a customization that uses BLC code to obtain the results of a Generic Inquiry and use that data to run a process.
The generic inquiry has several fields that are formulae and, for some reason, the returned results show null when they do, in fact, contain an amount - calculated by the formula:
Here's the BLC code to loop through the GI rows:
GIDesign gi = PXSelectReadonly<GIDesign, Where<GIDesign.name, Equal<Required<GIDesign.name>>>>.Select(this.Base, "ACAllocationBranches");
if (gi != null)
{
//Creates Generic Inquiry Graph for the specified inquiry
PXGenericInqGrph graph = PXGenericInqGrph.CreateInstance(gi.DesignID.Value);
//Set the filter...
graph.Caches[typeof(GenericFilter)].SetValueExt(graph.Filter.Current, "RefNbr", apinv.RefNbr);
//Loops through each returned result row of Generic Inquiry
foreach (GenericResult resultRow in graph.Views["Results"].SelectMulti())
{
GIMap.Clear();
//Loops through objects returned from one - not an object per field
foreach (string key in resultRow.Values.Keys)
{
//Loops through list of each object and the fields we need values from for each data key
foreach (GIResult resultMap in PXSelectReadonly<GIResult, Where<GIResult.designID, Equal<Required<GIResult.designID>>, And<GIResult.objectName, Equal<Required<GIResult.objectName>>>>>.Select(graph, new object[] { gi.DesignID.Value, key }))
{
//retrieves field value from data object specified
var result = graph.Caches[resultRow.Values[key].GetType()].GetValue(resultRow.Values[key], resultMap.Field);
//Load up the GIMap dictionary...
if(resultMap.IsActive == true)
GIMap.Add(key + "." + resultMap.Field, result);
}
}
}
}
Here's what the GI formula field looks like:
Unfortunately, these formula fields, like I mentioned, return null as a value in the Code, where the actual Generic Inquiry has numeric values.
Is it possible - or is there another way, through that BLC code example, to obtain those formula values?
Thanks much...
I suggest you to try this code:
public PXAction<SOOrder> Prc;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Prc")]
protected virtual IEnumerable prc(PXAdapter adapter)
{
GIDesign gi =
PXSelectReadonly<GIDesign, Where<GIDesign.name, Equal<Required<GIDesign.name>>>>.Select(this.Base,
"PMBudget");
var graph = PXGenericInqGrph.CreateInstance(gi.DesignID.Value);
List<bool> descs = new List<bool>(PXView.Descendings);
List<string> sorts = new List<string>(PXView.SortColumns);
int startRow = 0;
int totalRows = 0;
var result = graph.Results.View.Select(null, null, PXView.Searches, sorts.ToArray(), descs.ToArray(),
null, ref startRow, PXView.MaximumRows, ref totalRows)
.Select(x => (GenericResult)x);
return adapter.Get();
}
Results were like those:
How do you usually deal with Lists and the fact that they don't have a property to clearly identity an specific item ?
So far, the only solucion I could come up with is to put the key I use at the beginning, followed by a hyphen and the text that is shown on every item.
This way when I retrieve the text from the selected item I can get the key for the item.
This is how I do it, but surely there's gotta be a better solution and I'd really like that you could share your experience in this kind of scenarios.
Thanks in advance.
The picture ooks like you keep all the data managed in your application inside the text of the items of a standard list.
Better hava a separate class for the data container objects and an overview screen derived from List that takes an array of those container objects and instantiate the Items from that. This screen could then provide a method
DataContainer getSelectedObject()
which uses getSelectedIndex() internally to look up the object.
More specifically (Overview.java)
package mvc.midlet;
import javax.microedition.lcdui.List;
public class Overview extends List {
private final DomainObject[] data;
public static Overview create(DomainObject[] data) {
int i = 0;
for(; i < data.length; i++) {
if(data[i] == null) break;
}
String[] names = new String[i];
for(int j = 0; j < i; j++) {
names[j] = data[j].name;
}
return new Overview(names, data);
}
protected Overview(String names[], DomainObject[] data) {
super("Overview", IMPLICIT, names, null);
this.data = data;
}
public DomainObject getSelectedObject() {
return data[this.getSelectedIndex()];
}
}
I have foreach loop to listView control, I want to create objects for every listView content, so i want to change the name of the object incrementally by foreach loop
foreach (var item in listViewStates.Items)
{
State s = new State
{
ID = MaxStateID,
Name = listViewStates.Items[0].Text,
WorkflowID = MaxWFID,
DueDate = Convert.ToInt32(listViewStates.SelectedItems[0].SubItems[1].Text),
Priority = Convert.ToInt32(listViewStates.SelectedItems[0].SubItems[2].Text),
RoleID = Convert.ToInt32(listViewStates.SelectedItems[0].SubItems[3].Text),
Status =Convert.ToInt32(listViewStates.SelectedItems[0].SubItems[4].Text)
};
i++;
}
the variable is s from the State Class
You might have the wrong approach. What you need to do with your state object is add it to a collection, and work it from there. It's much easier to track this way.
Example with a local list for use after the loop, in the function:
public void MyFunction()
{
List<State> states = new List<State>();
foreach (var item in listViewStates.Items)
{
State s = new State
{
//Set state properties
};
states.Add(s);
}
//Use your states here, address with brackets
//states[0].ID ...
}
Example with a class-level list for later use outside the function:
List<State> _states;
public void MyFunction()
{
_states = new List<State>();
foreach (var item in listViewStates.Items)
{
State s = new State
{
//Set state properties
};
_states.Add(s);
}
//Now, after calling the function, your states remain
//You can address them the same way as above, with brackets
//_states[0].ID ...
}
I'm trying to convert an IQueryable object to a DataTable. Here's an example of a query that I would like to convert to a DataTable:
var query = DbContext.SomeObjectSet.Select(x => new { PropertyName1 = x.ColumnName1, PropertyName2 = x.ColumnName2 });
Please note the anonymous object that is created in the Select method and the names of the properties:
new { PropertyName1 = x.ColumnName1, PropertyName2 = x.ColumnName2 }
After Googling this issue, I came across the following code that converts an IQueryable object to a DataTable:
public static DataTable EntityToDatatable(this IQueryable result)
{
ObjectQuery query = (result as ObjectQuery);
ObjectContext context = query.Context;
EntityConnection entityCon = (context.Connection as EntityConnection);
using (SqlConnection sqlCon = new SqlConnection(entityCon.StoreConnection.ConnectionString))
{
using (SqlCommand cmd = new SqlCommand(query.ToTraceString(), sqlCon))
{
foreach (var param in query.Parameters)
{
cmd.Parameters.AddWithValue(param.Name, param.Value);
}
using (SqlDataAdapter dataAdapter = new SqlDataAdapter(cmd))
{
using (DataTable dataTable = new DataTable())
{
dataAdapter.Fill(dataTable);
return dataTable;
}
}
}
}
}
The code above "works" and the SQL statement from the ToTraceString() method is as follows:
SELECT [Extent1].[ColumnName1] AS [ColumnName1], [Extent1].[ColumnName2] AS [ColumnName2] FROM [dbo].[TableName] AS [Extent1]
Problem: The column names of the SQL statement (i.e. columnName1 and columnName2) do not correspond to the names of the properties of the objects (i.e. PropertyName1 and PropertyName2) that would be materialized if a ToList() or AsEnumerable() method was called on the query. This wouldn't be so bad if the SQL statement columns were in the same order as the anonymous object properties...but, this is not always the case. Somewhere (I guess inside of the IQueryable object) there must be a mapping between the SQL statement column names and the resulting anonymous object property names.
Does anyone know how to get at this mapping?
I've managed to find a solution to my problem:
First, you need the following code (from How does Entity Framework manage mapping query result to anonymous type?) which maps the positions of my anonymous object properties to the SQL statement column position:
public static Int32[] GetPropertyPositions(this ObjectQuery query)
{
// get private ObjectQueryState ObjectQuery._state;
// of actual type internal class
// System.Data.Objects.ELinq.ELinqQueryState
Object queryState = GetProperty(query, "QueryState");
AssertNonNullAndOfType(queryState, "System.Data.Objects.ELinq.ELinqQueryState");
// get protected ObjectQueryExecutionPlan ObjectQueryState._cachedPlan;
// of actual type internal sealed class
// System.Data.Objects.Internal.ObjectQueryExecutionPlan
Object plan = GetField(queryState, "_cachedPlan");
AssertNonNullAndOfType(plan, "System.Data.Objects.Internal.ObjectQueryExecutionPlan");
// get internal readonly DbCommandDefinition ObjectQueryExecutionPlan.CommandDefinition;
// of actual type internal sealed class
// System.Data.EntityClient.EntityCommandDefinition
Object commandDefinition = GetField(plan, "CommandDefinition");
AssertNonNullAndOfType(commandDefinition, "System.Data.EntityClient.EntityCommandDefinition");
// get private readonly IColumnMapGenerator EntityCommandDefinition._columnMapGenerator;
// of actual type private sealed class
// System.Data.EntityClient.EntityCommandDefinition.ConstantColumnMapGenerator
Object columnMapGenerator = GetField(commandDefinition, "_columnMapGenerator");
AssertNonNullAndOfType(columnMapGenerator, "System.Data.EntityClient.EntityCommandDefinition+ConstantColumnMapGenerator");
// get private readonly ColumnMap ConstantColumnMapGenerator._columnMap;
// of actual type internal class
// System.Data.Query.InternalTrees.SimpleCollectionColumnMap
Object columnMap = GetField(columnMapGenerator, "_columnMap");
AssertNonNullAndOfType(columnMap, "System.Data.Query.InternalTrees.SimpleCollectionColumnMap");
// get internal ColumnMap CollectionColumnMap.Element;
// of actual type internal class
// System.Data.Query.InternalTrees.RecordColumnMap
Object columnMapElement = GetProperty(columnMap, "Element");
AssertNonNullAndOfType(columnMapElement, "System.Data.Query.InternalTrees.RecordColumnMap");
// get internal ColumnMap[] StructuredColumnMap.Properties;
// array of internal abstract class
// System.Data.Query.InternalTrees.ColumnMap
Array columnMapProperties = GetProperty(columnMapElement, "Properties") as Array;
AssertNonNullAndOfType(columnMapProperties, "System.Data.Query.InternalTrees.ColumnMap[]");
Int32 n = columnMapProperties.Length;
Int32[] propertyPositions = new Int32[n];
for (Int32 i = 0; i < n; ++i)
{
// get value at index i in array
// of actual type internal class
// System.Data.Query.InternalTrees.ScalarColumnMap
Object column = columnMapProperties.GetValue(i);
AssertNonNullAndOfType(column, "System.Data.Query.InternalTrees.ScalarColumnMap");
//string colName = (string)GetProp(column, "Name");
// can be used for more advanced bingings
// get internal int ScalarColumnMap.ColumnPos;
Object columnPositionOfAProperty = GetProperty(column, "ColumnPos");
AssertNonNullAndOfType(columnPositionOfAProperty, "System.Int32");
propertyPositions[i] = (int)columnPositionOfAProperty;
}
return propertyPositions;
}
static object GetProperty(object obj, string propName)
{
PropertyInfo prop = obj.GetType().GetProperty(propName, BindingFlags.NonPublic | BindingFlags.Instance);
if (prop == null) throw EFChangedException();
return prop.GetValue(obj, new object[0]);
}
static object GetField(object obj, string fieldName)
{
FieldInfo field = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
if (field == null) throw EFChangedException();
return field.GetValue(obj);
}
static void AssertNonNullAndOfType(object obj, string fullName)
{
if (obj == null) throw EFChangedException();
string typeFullName = obj.GetType().FullName;
if (typeFullName != fullName) throw EFChangedException();
}
static InvalidOperationException EFChangedException()
{
return new InvalidOperationException("Entity Framework internals has changed, please review and fix reflection code");
}
Then I can modify the EntityToDatatable method as follows:
public static DataTable EntityToDatatable(this IQueryable query)
{
SqlConnection sqlConnection = null;
SqlCommand sqlCommand = null;
SqlDataAdapter sqlDataAdapter = null;
DataTable dataTable = null;
try
{
ObjectQuery objectQuery = (query as ObjectQuery);
ObjectContext objectContext = objectQuery.Context;
EntityConnection entityConnection = (objectContext.Connection as EntityConnection);
sqlConnection = new SqlConnection(entityConnection.StoreConnection.ConnectionString);
sqlCommand = new SqlCommand(objectQuery.ToTraceString(), sqlConnection);
foreach (var parameter in objectQuery.Parameters)
{
sqlCommand.Parameters.AddWithValue(parameter.Name, parameter.Value);
}
sqlDataAdapter = new SqlDataAdapter(sqlCommand);
dataTable = new DataTable();
sqlDataAdapter.Fill(dataTable);
// Get the mapping between the object property position and
// the SQL statment column position.
Int32[] propertyPositions = objectQuery.GetPropertyPositions();
// Create a column name to column position (ordinal) lookup.
Dictionary<String, Int32> mapColumnNameToColumnPosition = new Dictionary<string, int>();
// Populate the lookup.
for (Int32 i = 0; i < propertyPositions.Length; ++i)
{
mapColumnNameToColumnPosition.Add(dataTable.Columns[propertyPositions[i]].ColumnName, i);
}
// Get the object's property information.
PropertyInfo[] pi = query.GetType().GetGenericArguments()[0].GetProperties();
// Iterate through the lookup and change the position of the datatable columns.
// The order of the datatable columns will now correspond to the order of the object
// properties.
foreach (var map in mapColumnNameToColumnPosition)
{
// Change the column position.
dataTable.Columns[map.Key].SetOrdinal(map.Value);
// Change the column name.
dataTable.Columns[map.Key].ColumnName = pi[map.Value].Name;
}
return dataTable;
}
catch (Exception ex)
{
// Something went wrong and we're going to raise an exception...we
// might as well dispose of the datatable if it exists because it's
// not going to be used.
if (dataTable != null) dataTable.Dispose();
throw new Exception("IQueryable to DataTable conversion error.", ex);
}
finally
{
// Do some cleanup on objects that are no longer needed.
if (sqlDataAdapter != null) sqlDataAdapter.Dispose();
if (sqlCommand != null) sqlCommand.Dispose();
if (sqlConnection != null) sqlConnection.Dispose();
}
}
This is an interesting error I've come across while implementing IEnumerable on a class. It appears to be similar to an "access to modified closure" issue, but I'm at a loss as to how to fix it.
Here is a simple example that demonstrates the issue:
void Main()
{
var nodeCollection = new NodeCollection();
nodeCollection.MyItems = new List<string>() { "a", "b", "c" };
foreach (var node in nodeCollection)
{
node.Dump();
}
}
public class NodeCollection : IEnumerable<Node>
{
public List<string> MyItems;
public IEnumerator<Node> GetEnumerator()
{
// This isn't necessary, but it should prove that it's not an "access to modified closure" issue.
var items = MyItems;
for (var i = 0; i < 3; i++)
{
var node = new Node();
// I want the node to contains the items in MyItems.
node.Items = items;
// Plus an additional item. Note that I am adding the item to the node, NOT to MyItems.
node.Items.Add(string.Format("iteration: {0}", i));
yield return node;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class Node
{
public List<string> Items;
}
As you can see from the Dump() statement, I'm running this in LINQPad, but the issue will present itself in any IDE.
When I run the snippet, I get the following output:
Because I am adding the item to Items in the newly instantiated Node, I would NOT expect the item to be added to MyItems, but this is obviously what is occurring.
It seems that Items in Node is pointing to MyItems in NodeCollection.
Can anyone tell me:
Why this is happening?
How to make it not happen?
You are creating new nodes each iteration, but then setting the same items instance to the Items property of each node. Then you are adding the iteration string to the items instance stored in the Items collection (which is always the same instance), resulting in each subsequent node having more and more "iteration" entries. If you kept all of the nodes, you'd find that all of them have exactly the same Items value.
I think the basic misunderstanding here was that you were assuming that setting the Items property of the Node (node.Items = items;) would copy the items list into the node. In fact, all it does is set node.Items to point to the already-existing list that you call items.
This should give you an idea where you went wrong:
// This same instance of items is being reused each time
var items = MyItems;
for (var i = 0; i < 3; i++)
{
var node = new Node();
// I want the node to contains the items in MyItems.
// Assuming node.Items is a List<String>
node.Items = new List<String>();
node.Items.AddRange(items);
node.Items.Add(string.Format("iteration: {0}", i));
yield return node;
}
node.Items = items; sets node.Items to be a reference to the items list. There is just one list, with several references to it.
I suppose that what you want is to have a separate list in each node and that you want to copy the elements in items into that list. To do that, create a new list wich contains all of the elements from items.
node.Items = new List<string>(items);
When you do:
var item = MyItems;
you just create a reference to MyItems and store it in the variable item. Then when you do:
node.Items = items;
you just take the same reference and store it in node.Items. If you need node.Items to be a new list (point to a different memory location) initialize it again.
node.Items = new List();