I have the code below. To explain there will always be values for the 'tl' variable.
At the moment its hard coded to always assume 4 columns in the row, but I want to make it work based on the count of the columns and make it build the levels based on how many columns there are, but there also needs to be a value in the column.
So at the moment if there is a value in column 2, it will build the 'ltwo' variable, and then if there is a value in column 3 it does the 'lthree'.
I want to make it build as many levels as it needs to so im not repeating code and having the same code over and over.
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
foreach (DataRow row in dataTable.Rows)
{
var tl = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
if (!String.IsNullOrEmpty(row[2].ToString()))
{
var ltwo = new AdditionalPropertyType
{
Name = row[2].ToString()
};
var ltwolist = new List<AdditionalPropertyType>();
ltwolist.Add(tl);
ltwo.AdditionalProperties = ltwolist;
if (!String.IsNullOrEmpty(row[3].ToString()))
{
var lthree = new AdditionalPropertyType
{
Name = row[3].ToString()
};
var lthreelist = new List<AdditionalPropertyType>();
lthreelist.Add(ltwo);
lthree.AdditionalProperties = lthreelist;
currentadditionalproperties.Insert(0, lthree);
}
else
currentadditionalproperties.Insert(0, ltwo);
}
else
currentadditionalproperties.Insert(0, tl);
}
return currentadditionalproperties;
}
You can get the columns using the Columns property of the DataTable:
foreach (DataRow row in dataTable.Rows)
{
foreach(DataColumn column in dataTable.Columns)
{
Trace.WriteLine(column.ColumnName + " = " + row[column]);
}
}
You probably want to do something like this: (written on the websites, some minor typos can be present)
You need to iterate the additional columns and check if there is a value present. When there is a value, create a backup reference and renew your property.
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
// check if there are atleast 2 columns defined
if(dataTable.Columns.Count < 2)
throw new Exception("At least two columns are required");
// The result
var currentadditionalproperties = new List<AdditionalPropertyType>();
// iterate the rows
foreach (DataRow row in dataTable.Rows)
{
// create the base property
var tl = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
// check the rest of the columns for additional names
foreach(int index=2;index<dataTable.Columns.Count;index++)
{
var columnValue = row[index].ToString();
// if the column is empty, discontinue the iteration
if(String.IsNullOrEmpty(columnValue))
break;
// create a backup reference.
var previous = tl;
// create a new AdditionalPropertyType
var tl = new AdditionalPropertyType { Name = columnValue };
// Create the list
tl.AdditionalProperties = new List<AdditionalPropertyType>();
// add the previous (backup reference)
tl.AdditionalProperties.Add(previous);
}
// insert the 'chain' of additional properties on the list at possition 0
currentadditionalproperties.Insert(0, tl);
}
// return the list
return currentadditionalproperties;
}
The first step is to reverse your condition and make use of the keyword continue
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
foreach (DataRow row in dataTable.Rows)
{
var tl = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
if (String.IsNullOrEmpty(row[2].ToString())){
currentadditionalproperties.Insert(0, tl);
continue;
}
var ltwo = new AdditionalPropertyType
{
Name = row[2].ToString()
};
var ltwolist = new List<AdditionalPropertyType>();
ltwolist.Add(tl);
ltwo.AdditionalProperties = ltwolist;
if (String.IsNullOrEmpty(row[3].ToString())) {
currentadditionalproperties.Insert(0, ltwo);
continue;
}
var lthree = new AdditionalPropertyType
{
Name = row[3].ToString()
};
var lthreelist = new List<AdditionalPropertyType>();
lthreelist.Add(ltwo);
lthree.AdditionalProperties = lthreelist;
currentadditionalproperties.Insert(0, lthree);
}
return currentadditionalproperties;
}
Now, the code is clearer. The next step is to collect the repeating cases. Note the second case onward is repeating. Thus, do further simplification:
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
foreach (DataRow row in dataTable.Rows)
{
var tlprev = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
bool isTlUpdated = true;
for (int i = 2; i <= 3; ++i) { //change this according to your need
if (String.IsNullOrEmpty(row[i].ToString()) && isTlUpdated){
currentadditionalproperties.Insert(0, tlprev);
isTlUpdated = false;
break; //note that this will now change to break to break from the current for-loop
}
var lnext = new AdditionalPropertyType
{
Name = row[i].ToString()
};
var lnextlist = new List<AdditionalPropertyType>();
lnextlist.Add(tlprev);
lnext.AdditionalProperties = lnextlist;
tlprev = lnext; //need to record this for the next loop or end of the case
isTlUpdated = true;
}
if (isTlUpdated) //correction by Jeroen
currentadditionalproperties.Insert(0, tlprev);
}
return currentadditionalproperties;
}
The key is to simplify the code step-by-step
You haven't posted all your code, so I had to guess in a couple of places (such as what the "currentAdditionalProperties" does).
I think that the below code illustrates what you want to do by making the logic extendable depending on how many columns the data table has.
The trick is to just store the "last thing" in a variable, so it can be used for the "current thing". At the end, whatever was the "last thing" is what you want to store in your "currentAdditionalProperties" object. I have commented so you can see the logic.
private List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable)
{
AdditionalPropertyType lastNewType; // to remember the previous new instance
// for all rows...
foreach (DataRow row in dataTable.Rows)
{
// the first type takes name and value from the first two fields
AdditionalPropertyType newType = new AdditionalPropertyType();
newType.Name = row[0].ToString();
newType.Value = row[1].ToString();
// remember this type: it is used as the AdditionalProperties for the NEXT type
lastNewType = newType;
// additional types start from field 2
int field = 2;
// iterate until we find a NULL field.
// If you want to check for the end of the fields rather than a NULL value, then instead use:
// while(field < dataTable.Columns.Count)
while(!String.IsNullOrEmpty(row[field].ToString()))
{
// create new type
var newSubType = new AdditionalPropertyType();
// get name
Name = row[field].ToString();
// new type takes the PREVIOUS type as its additional parameters
List<AdditionalPropertyType> propertyData = new List<AdditionalPropertyType>();
propertyData.Add(lastNewType);
newSubType.AdditionalProperties = propertyData;
// remember THIS type for the NEXT type
lastNewType = newSubType;
// process next field (if valid)
field++;
}
// put the last set of properties found into the current properties
currentAdditionalProperties.Insert(0, lastNewType);
return currentAdditionalProperties;
}
}
Related
I got a result of a query in nHibernate:
var result = _Session.CreateSQLQuery("SELECT 'just a string' as Type, NAME FROM SCHEMA.PERSON where NAME like ('%A%')").List();
and I want to show this result in a DataGridView. So I tried:
this.results.DataSource = result;
But this does not to work (It shows just a lot of stuff like "Length", "Long Length", "Rank" and so aon but not the actual sql result) as result is of type: System.Collections.IList System.Collections.Generic.List and in fact seems like a object array inside a object array.
So I tried:
this.results.DataSource = from res in result.Cast<List<object[]>>()
select new
{
T = res[0][0],
V = res[0][1]
};
but this only shows an empty control.
So how to show the results and as an advanced task how to show the alias/select result names as Column Headers?
btw. This should work for every SQL. So I cant use mappingFiles.
After a lot of search and help from this question: NHibernate output columns/projections of CreateQuery().list()
I found a solution that works for me:
var query = _Session.CreateSQLQuery("SELECT 'just a string' as Type, NAME FROM SCHEMA.PERSON where NAME like ('%A%')").SetResultTransformer(Transformers.AliasToEntityMap);
var result = query.List();
var tab = new DataTable();
if (result.Count > 0)
{
var asHash = result[0] as Hashtable;
foreach (DictionaryEntry item in asHash)
{
tab.Columns.Add(item.Key as string);
}
foreach (Hashtable item in result)
{
var newobj = new Object[tab.Columns.Count];
int i = 0;
foreach (DictionaryEntry row in item)
{
newobj[i]= row.Value;
i++;
}
tab.Rows.Add(newobj);
}
}
this.results.DataSource = tab;
Im sure that there are other (better) ways to do it, but hey: It works.
The other solutions do not preserve the projection order. I implemented this (ugly) data transformer to keep the order:
Usage example:
var command = "SELECT 'just a string' as Type, NAME FROM SCHEMA.PERSON where NAME like ('%A%')";
var query = _Session.CreateSQLQuery(command)
.SetResultTransformer(new DataTableTransformer());
var dataTable = (DataTable)query.List()[0];
Implementation:
public class DataTableTransformer : IResultTransformer
{
bool _isHeaderRowAdded;
public object TransformTuple(object[] tuple, string[] aliases)
{
if (false == _isHeaderRowAdded)
{
_isHeaderRowAdded = true;
return new Tuple<string[], object[]>(aliases, tuple);
}
return tuple;
}
public IList TransformList(IList collection)
{
if (collection.Count == 0)
{
return new []{new DataTable()};
}
var dataTable = new DataTable();
var headerAndFirstRow = (Tuple<string[], object[]>)collection[0];
foreach (var columnName in headerAndFirstRow.Item1)
{
dataTable.Columns.Add(columnName);
}
var newRow = dataTable.NewRow();
newRow.ItemArray = headerAndFirstRow.Item2;
dataTable.Rows.Add(newRow);
int index = 0;
foreach (var item in collection)
{
if (index++ == 0) continue;
var dataRow = dataTable.NewRow();
dataRow.ItemArray = (object[])item;
dataTable.Rows.Add(dataRow);
}
return new []{dataTable};
}
}
I can't work out how to get the Type of a LINQ result. My set contains both strings and bools, so I run into trouble when I try to act on the rows. I attached an incredibly rough workout using try/catch (for a laugh), but it hurts my soul and would much rather know the proper method in obtaining the Type.
private AppointmentInfoClass UpdateDataContext(DataSet phaseDataSet) {
var phaseCollection = new AppointmentInfoClass();
var Type = phaseCollection.GetType();
var properties = Type.GetProperties();
var result = from DataRow myRow in DataBindings.CompanyAppsDataSet.Tables[0].Rows
where (int)myRow["AppointmentID"] == ApptID
select myRow;
var k = 0;
foreach (DataRow row in phaseDataSet.Tables[0].Rows) {
string header;
header = row.Field<string>("Header");
foreach (var field in result) {
try {
properties[k].SetValue(phaseCollection, field.Field<string>(header));
}
catch (Exception) {
properties[k].SetValue(phaseCollection, field.Field<bool>(header).ToString());
}
}
k++;
}
return phaseCollection;
}
It will return the type you have written instead of Type
string s = field.Field<string>("ColumnName");
bool b = field.Field<bool>("ColumnName");
I have a DataTable from which I would like to loop through each row and column and then select a value from a specific column depending on the other values in the columns/each row.
My code currently looks like this:
foreach (DataRow drow in dt.Rows)
{
foreach (DataColumn dcol in dt.Columns)
{
foreach (var Item in ImportData)
{
if (Item.Value.Equals(true))
{
if (Item.Key.Equals("" + dcol))
{
string value = drow[dcol].ToString();
if (value.Equals("X"))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
}
}
}
}
ImportData is a Dictionary<string, bool>, which holds the data that I want to compare with my DataTable.
string outDraws is just a string which should hold the content of the drawings I want to print out.
My problem now is that I only want to print out the content in the column 'Drawing' of the row where all columns with the same name as the Keys in ImportData have 'X' as value. At the moment I'm getting all the rows where any of the columns have 'X' as value and has the same name as any Key in ImportData.
I understand that it will be quite hard for you to get what I want to do but please ask if you need any more information and I will try to provide.
Many thanks in advance.
Edit:
ImportData contains the name of different products as keys. These products have either been selected or not by the customer through another program, if they have been selected they have the value true and if not selected they have the value false.
With the method presented above I would like to compare ALL the keys that have the value true with the column names in the DataTable. If the column name corresponds to the key in ImportData (which is the name of a product) then I want to check if that column in a specific row has 'X' as value.
This goes on for ALL the keys in ImportData and in the end I should know which row in the DataTable that has an 'X' in all the columns with the same name as the keys in ImportData. For this row I would like to get the content of the column called 'Drawing'.
So for an example say that ImportData contains:
[Motor, true][Product6, true][Product7, true]
Then I would like to print out the column Drawing at row 6.
Unfortunately I can't post pictures..
As with any problem: divide and conquer. Break down your problem in smaller pieces and go from there.
From what I understand, you want to do something with certain rows from the datatable. Something like:
foreach (var drow in dt.Rows.OfType<DataRow>())
{
if (SomeConditionIsMet(dt, drow, ImportData))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
The function SomeConditionIsMetcould looks like this:
private static bool SomeConditionIsMet(
DataTable dt, DataRow drow,
IDictionary<string, bool> importData)
{
// TODO if the condition is met, return true
// otherwise, return false
}
Now your problem is simplified to thinking about what it means that 'Some condition is met'. Once you can clearly express that in words, rename the function to reflect that (e.g. to 'AllColumnsAreChecked')
Here's a sample with solution as I understand it:
internal class Program
{
private static void Main(string[] args)
{
var importData = new Dictionary<string, bool>()
{
{"Product1", true},
{"Product2", false},
{"Product3", true},
};
var dt = new DataTable();
dt.Columns.Add("Product1");
dt.Columns.Add("Product2");
dt.Columns.Add("Product3");
dt.Columns.Add("Product4");
dt.Columns.Add("Drawing");
// row1 should be added
var row1 = dt.NewRow();
row1["Product1"] = "X";
row1["Product3"] = "X";
row1["Drawing"] = "Drawing1";
dt.Rows.Add(row1);
// row2 should not be added
var row2 = dt.NewRow();
row2["Product1"] = "X";
row2["Drawing"] = "Drawing2";
dt.Rows.Add(row2);
string outDraws = string.Empty;
foreach (DataRow drow in dt.Rows.OfType<DataRow>())
{
if (AllColumnsAreChecked(drow, importData))
{
outDraws += drow["Drawing"].ToString();
outDraws += "\n";
}
}
Console.WriteLine(outDraws);
}
private static bool AllColumnsAreChecked(DataRow drow, Dictionary<string, bool> importData)
{
foreach (var key in importData.Keys)
{
if (!importData[key])
continue;
var value = drow[key] as string;
if (value != "X")
return false;
}
}
}
Bonus: here's a LINQ based implementation of the check:
private static bool AllColumnsAreChecked(DataRow drow, Dictionary<string, bool> importData)
{
return importData.Keys
.Where(k => importData.ContainsKey(k) && importData[k]) // the field must be enabled in importData
.All(k => (drow[k] as string) == "X"); // the corresponding value in the row must be 'X'
}
Try this
DataTable tbl = new DataTable();
foreach (DataRow row in tbl.Rows)
{
object cellData = row["colName"];
}
I have a IEnumerable<object> dataSource which contains a collection anonymous types. The actual structure of the anonymous type won't be known at design time, so I'm trying to find a generic solution that can handle any anonymous type.
How can I load them into epplus to create a spreadsheet? I have a worksheet called ws and I tried:
ws.Cells["A1"].LoadFromCollection(dataSource, true);
However when that runs it outputs all of the anonymous type's properties into a single cell:
{ Id = 10000, Title = This is a test }
I've tried passing in MemberInfo using:
var members = dataSource.First().GetType().GetMembers();
ws.Cells["A1"].LoadFromCollection(this._dataSource, true,
TableStyles.Medium1, BindingFlags.Public, members);
But that throws an exception:
Supplied properties in parameter Properties must be of the same type as T
Any suggestions on how I can create a spreadsheet using anonymous types in c#?
I have tested
using (var excel = new OfficeOpenXml.ExcelPackage())
{
var sheet = excel.Workbook.Worksheets.Add("Test");
sheet.Cells["A1"].LoadFromCollection(dataSource, true);
excel.SaveAs(new FileInfo(#"C:\Temp\Test.xlsx"));
}
with this sample data:
var dataSource = Enumerable.Range(1, 100).Select(i => new{ ID=i, Title="Title " + i });
It works fine. It creates two columns with the correct headers and 100 rows.
But you should use anonymous types only if you know the structure at compile time.
You could use a DataTable and LoadFromDataTable instead. Since i don't know how you create the anonymous type i show you just a small sample:
DataTable dataSource = new DataTable();
dataSource.Columns.Add("Id"); // default type is string
dataSource.Columns.Add("Title");
// add other columns
dataSource.Rows.Add("1", "Title1");
// add other rows
using (var excel = new OfficeOpenXml.ExcelPackage())
{
var sheet = excel.Workbook.Worksheets.Add("Test");
sheet.Cells["A1"].LoadFromDataTable(dataSource, true);
excel.SaveAs(new FileInfo(#"C:\Temp\Test.xlsx"));
}
You could group the anonymous types to make it easier for exporting with dataTables. The bug "Supplied properties in parameter Properties must be of the same type as T" is still there and a workaround is using DataTables.
// Imagine list is your main datasource
IEnumerable<object> list = Enumerable.Empty<object>(); // Data Source of <object>
// Added anon types at runtime added to the object list
var anonTypesOne = new object[]
{
new { GuidID = Guid.NewGuid(), StringProperty = "the string property" },
new { IntegerID = 1, IntegerProperty = 99 }
};
var anonTypesTwo = new object[]
{
new { StringID = "1", BooleanProperty = true, NumberProperty = 3, StringProperty = "Four" },
new { GuidID = Guid.NewGuid(), NumberThree = 3 },
new { GuidID = Guid.NewGuid(), NumberThree = 3 },
new { GuidID = Guid.NewGuid(), NumberThree = 3 }
};
list = list.Concat(anonTypesOne).Concat(anonTypesTwo);
// Grouping works on anon types so we can group the export into their own tables
var groupings = list.GroupBy(i => i.GetType());
using(var package = new ExcelPackage(new FileInfo("C:\\Temp\\Anon.xlsx")))
{
var ws = package.Workbook.Worksheets.Add("Anonymous Types");
// add each "anon type matched grouping"
foreach(var grouping in groupings)
{
var isNew = ws.Dimension == null; // the sheet is empty if Dimension is null.
var row = 0;
if(isNew)
{
row = 1; // start from the first row
}
else
{
// otherwise there are tables already, start from the bottom
row = ws.Dimension.End.Row;
}
// because of EPP inheritance bug of T, we can just use dataTable
DataTable dt = new DataTable(grouping.Key.Name);
var properties = grouping.Key.GetProperties(); // Get anon type Properties
foreach(var property in properties)
{
dt.Columns.Add(property.Name);
}
foreach(var item in grouping.ToList())
{
var dataRow = dt.NewRow();
foreach(var p in properties) // populate a single row
{
dataRow[p.Name] = p.GetValue(item); // item is anon object instance
}
dt.Rows.Add(dataRow);
}
if(isNew) // load into the top most left cell of the worksheet
ws.Cells[1, 1].LoadFromDataTable(dt, PrintHeaders: true);
else // load from the dimension of current items + 1 row for spacing
ws.Cells[ws.Dimension.End.Row + 1, 1].LoadFromDataTable(dt, PrintHeaders: true);
ws.InsertRow(ws.Dimension.End.Row + 2, 5); // Insert some padding between each group
}
package.Save();
}
I was, this thread is older, but I'm looking for the same problem.
With the following code (VB) I have success.
Carsten
Dim targetFile = New IO.FileInfo(sFN)
Dim dataSource = Enumerable.Range(0, 1).Select(Function(i) New With {.ID = 1000, .Titel = "This is a test "}).ToList
Using epp = New OfficeOpenXml.ExcelPackage(targetFile)
Dim ws = epp.Workbook.Worksheets.Add("lst_Anonymous")
ws.Cells(1, 1).LoadFromCollection(dataSource, True,
OfficeOpenXml.Table.TableStyles.Medium1,
Reflection.BindingFlags.Public,
dataSource.GetType.GetGenericArguments()(0).GetProperties)
epp.Save()
End Using
I have 2 lists..
The first contains rows with mapping values inlcuding column name, xcord, ycord
The second contains the data I need to map..
I need to get the value in each row using the column name from the first row..
for example
List<SheetMappings> smaps = new List<SheetMappings>();
foreach(maplist m in mlist)
{
SheetMappings newMap = new SheetMappings();
foreach(vallist v in vlist)
{
newMap.Value = v.{m.ColumnName};
newMap.xCord = m.xCord;
newMap.yCord = m.yCord;
}
smaps.Add(newMap);
}
Any assitance appreciated
Cheers
Graham
EDIT:
List<SpreadMappings> spreadMapping = new List<SpreadMappings>();
foreach (var m in mappings)
{
foreach (var v in hvalues)
{
SpreadMappings map = new SpreadMappings();
switch (m.ColumnName)
{
case “DocHeading”:
map.ColumnX = m.ColumnX;
map.ColumnY = m.ColumnY;
map.ColumnValue = v.DocHeading;
map.ColumnName = m.ColumnName;
map.ColumnId = v.Id;
map.ColumnSheetName = sheetName; spreadMapping.Add(map);
break;
If I understand what you're trying to do, you'll need to use reflection to get the value of the property represented by m.ColumnName:
var smaps = new List<SheetMappings>();
foreach(maplist m in mlist)
{
var pi = typeof(vallist).GetProperty(m.ColumnName);
var newMap = new SheetMappings();
foreach(vallist v in vlist)
{
newMap.Value = pi.GetValue(v, null);
newMap.xCord = m.xCord;
newMap.yCord = m.yCord;
}
smaps.Add(newMap);
}
So that's using reflection to get a reference to the PropertyInfo for the property represented by m.ColumnName, then calling PropertyInfo.GetValue to get the value of that property from v.
Well I think the "newMap.Value = v.{m.ColumnName}" part would be something like:
newMap.Value = v.FirstOrDefault( vitem => vitem.ColumnName == m.ColumnName );
This would give you the first item within "v" that has a "ColumnName" property that matches the "ColumnName" property of "m". This assumes that the contents of "vallist" are objects that have a "ColumnName" property.