Converting Datatable to ienumerable<anonymous>? - c#

is it possible to convert data table to ienumerable without know its class name.
my requirement is to convert table
First | Last
--------------
john | mcgill
clara | linda
to
{{First:john,Last:mcgill},{First:clara ,Last:linda}}
Ienumerable collection
i dont want to use dynamic object because dynamic object supports only frame work 4.
thanks

var results = from row in dataTable.AsEnumerable()
select new {
First = row.Field<string>("First"),
Last = row.Field<string>("Second")
};
You'll need System.Data.DataSetExtensions.

You can use Anonymous Types - they were introduced with .NET 3.5.
Syntax for that kind of objects is really clear and intuitive:
var item = new { First = "First-Value", Last = "Last-Value" }
and the query:
var items = dataTable.AsEnumerable()
.Select(i => new {
First = i.Field<string>("First"),
Last= i.Field<string>("Last")
});

No column names please!
public string ConvertDataTableToString(DataTable table)
{
int iColumnCount = table.Columns.Count;
int iRowCount = table.Rows.Count;
int iTempRowCount = 0;
string strColumName = table.Columns[0].ColumnName;
string strOut = "{";
foreach (DataRow row in table.Rows)
{
strOut = strOut + "{";
foreach (DataColumn col in table.Columns)
{
string val = row.Field<string>(col.ColumnName);
strOut = strOut + col.ColumnName + ":" + val;
if (col.Ordinal != iColumnCount - 1)
{
strOut = strOut + ",";
}
}
strOut = strOut + "}";
iTempRowCount++;
if (iTempRowCount != iRowCount)
{
strOut = strOut + ",";
}
}
strOut = strOut + "}";
return strOut;
}

It is a fairly easy job using anonymous types. Here is a complete example that only requires classes from the System.Linq and System.Data namespaces:
class Program
{
static void Main(string[] args)
{
DataTable dataTable = new DataTable();
dataTable.Columns.Add().ColumnName = "First";
dataTable.Columns.Add().ColumnName = "Last";
var row = dataTable.NewRow();
row["First"] = "hello";
row["Last"] = "world";
dataTable.Rows.Add(row);
var query = dataTable.Rows.Cast<DataRow>()
.Select(r => new
{
First = r["First"],
Last = r["Last"]
});
foreach (var item in query)
Console.WriteLine("{0} {1}", item.First, item.Last);
}
}

Related

how to skip multiple iterations and improve code

In the below C# code, I am doing 2 iterations over data and trying to create a filter that looks like this,
(Name1 = 'd1' AND Name2 = 'd1') OR (Name1 = 'd3' AND Name2 = 'd3') OR (Name1 != 'd1' AND Name1 != 'd3')
For the below sets of data:
var data = new List<(string, string)>
{
("d1", "d2"), ("d3", "d4"),
};
Here is code:
foreach(var entity in data)
{
filter += $"(Name1 = '{entity.Item1}' AND Name2 = '{entity.Item1}') OR ";
}
filter = filter + "(";
foreach (var entity in data)
{
filter += $"Name1 != '{entity.Item1}' AND ";
}
//remove last "AND"
var final = filter.Substring(0, filter.LastIndexOf("AND")).TrimEnd() + ")";
How I can improve this code and get rid of 2 iterations? Thanks.
Use Linq and string.Join to iterate once and avoid SubString:
var data = new List<(string, string)> { ("d1", "d2"), ("d3", "d4"), };
var exclude = new List<string>();
var filters = string.Join(" OR ", data.Select(entity => {
exclude.Add($"Name1 != '{entity.Item1}'");
return $"(Name1 = '{entity.Item1}' AND Name2 = '{entity.Item1}')";
}));
string final = $"{filters} OR ({string.Join(" AND ", exclude)})";
You can have two filter strings which you build up simultaneously and then combine at the end:
string filter = "";
string filter2 = "";
var data = new List<(string, string)>
{
("d1", "d2"), ("d3", "d4"),
};
foreach(var entity in data)
{
filter += $"(Name1 = '{entity.Item1}' AND Name2 = '{entity.Item1}') OR ";
filter2 += $"Name1 != '{entity.Item1}' AND ";
}
filter2 = filter2.Substring(0, filter2.LastIndexOf("AND")).TrimEnd();
string final = $"{filter}({filter2})";
Live demo: https://dotnetfiddle.net/TGr6Jz

Datagrid does not show full Data of Datatable

I'm loading a DataTable from a Database and but do not want to have it the way it looks. That’s why I decided to create a new DataTable and fill it with my received Data.
Unfortunately, the data are not fully shown in the DataGrid. To be more detailed, only the first column is shown but all other are not. While debugging I could ensure that the DataTable is filled correctly.
Does anyone of you have an idea what I am doing wrong?
Model:
internal DataTable getValueTable(ObservableCollection<CheckableMenuitem> listSelectableValues)
{
DataTable TableToReturn = new DataTable();
string LaborValueColumnCaption = Properties.Resources.LabValueColumnCaption;
TableToReturn.Columns.Add(LaborValueColumnCaption);
List<string> selectedValueNames = new List<string>();
foreach (CheckableMenuitem item in listSelectableValues.Where(x => x.IsChecked == true))
{
selectedValueNames.Add(item.Caption);
}
string selectionFormular = "Wertname in ('" + string.Join("', '", selectedValueNames) + "')";
DataRow[] selectionRows = fullValueTable.Select(selectionFormular);
HashSet<DateTime> ListOfDays = new HashSet<DateTime>();
foreach (DataRow item in selectionRows)
{
DateTime? Tmp = Functions.ParseNullableDate(item["Messdatum"].ToString());
if (Tmp != null)
{
ListOfDays.Add(((DateTime)Tmp).Date);
}
}
string DateFormatString = "dd.MM.yyyy";
foreach (DateTime date in ListOfDays)
{
TableToReturn.Columns.Add(date.ToString(DateFormatString));
}
foreach (var valueName in selectedValueNames)
{
string singleSelectionFormular = "Wertname ='" + valueName + "'";
List<DataRow> singleSelectionRows = fullValueTable.Select(singleSelectionFormular).ToList();
if (singleSelectionRows.Count() == 1)
{
DateTime? ValueDate = Functions.ParseNullableDate(singleSelectionRows[0]["Messdatum"].ToString());
if (ValueDate != null)
{
}
DataRow newRow = TableToReturn.NewRow();
//erste Spalte
newRow[LaborValueColumnCaption] = valueName;
string spalte = ((DateTime)ValueDate).ToString(DateFormatString);
newRow[spalte] = singleSelectionRows[0]["MESSWERT_ALPHA"].ToString();
//newRow[((DateTime)ValueDate).ToString(DateFormatString)] = singleSelectionRows[0]["MESSWERT_ALPHA"].ToString();
TableToReturn.Rows.Add(newRow);
}
if (singleSelectionRows.Count() > 1)
{
List<DataRow> Rows = new List<DataRow>() { TableToReturn.NewRow() };
Rows[0][LaborValueColumnCaption] = valueName;
HashSet<DateTime> usedDates = new HashSet<DateTime>();
foreach (var item in singleSelectionRows)
{
DateTime? ValueDate = Functions.ParseNullableDate(singleSelectionRows[0]["Messdatum"].ToString());
if (ValueDate != null)
{
DateTime Date = ((DateTime)ValueDate).Date;
if (usedDates.Add(Date))
{
string name = "Wertname ='" + valueName + "' and Messdatum > " + Date + " and Messdatum < " + Date.AddDays(1);
DataRow[] test2 = fullValueTable.Select(singleSelectionFormular);
//Rows[0][((DateTime)ValueDate).ToString(DateFormatString)] = singleSelectionRows[0]["MESSWERT_ALPHA"].ToString();
}
}
}
foreach (DataRow newRow in Rows)
{
TableToReturn.Rows.Add(newRow);
}
}
}
return TableToReturn;
}
ViewModel:
public DataTable ValueTable
{
get
{
return model.getValueTable(ListSelectableValues);
}
}
View:
<DataGrid DockPanel.Dock="Top" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" AutoGenerateColumns="True" ItemsSource="{Binding Path=ValueTable}"/>
edit:
enter image description here
the problem lays on the date format:
wpf datatable column name can not have slash /
Cheers,
Marco

C# Plugin- Break up array on every second ","

I have made a FetchXML query that returns data from my CRM2013 grid
The data is passed into a list
Which is then turned into a CSV file with the following code;
EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetchxml)); foreach (var c in result.Entities)
{
if (result != null && result.Entities.Count > 0)
{
List<string> _product = new List<string>();
foreach (Entity _entity in result.Entities)
{
_product.Add(((EntityReference)c.Attributes["productid"]).Name.ToString());
_product.Add(_entity.Attributes["quantity"].ToString());
}
CSVFile = string.Join(",", _product.ToArray());
string AddressPath = "FM-OR" + "_";
string AddressSavePath = #"\\fm\CRMdata\maesteg\" + AddressPath + ".csv";
System.IO.File.WriteAllText(AddressSavePath, CSVFile.ToString());
The output would be as follows
ProductExample1, 1.0, ProductExample2, 4.0, ProductExample3, 2.0
What I want is the output to now be
ProductExample1, 1.0
ProductExample2, 4.0
ProductExample3, 2.0
Any suggestions at how I would achieve this?
Thanks
Update
Didn't feel like I quite explained it properly
With the output I want, I want them to be separate strings so that I can output a different CSV file for each Product + Quantity in the list
Thanks
Update
Code with new suggestions implemented, error is described in comment below
EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetchxml)); foreach (var c in result.Entities)
{
if (result != null && result.Entities.Count > 0)
{
List<string> _product = new List<string>();
foreach (Entity _entity in result.Entities)
{
string productid = (((EntityReference)_entity.Attributes["productid"]).Name.ToString());
string quantity = _entity.Attributes["quantity"].ToString();
CSVFile = productid + "," + quantity;
int n =1;
string AddressPath = "FM-OR" + "_" +actualID + "_" + n;
string AddressSavePath = #"\\fm\CRMdata\maesteg\" + AddressPath + ".csv";
System.IO.File.WriteAllText(AddressSavePath, CSVFile.ToString());
n++;
}
}
}
Thanks
From what I understand of your question, this might help you. CSVFILE now is a string containing the productid and quantity separated by a "," which you can write to your file.
EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetchxml));
if (result != null && result.Entities.Count > 0)
{
List<string> _products = new List<string>();
foreach (Entity _entity in result.Entities)
{
string productid = (EntityReference)c.Attributes["productid"]).Name.ToString();
string quantity = _entity.Attributes["quantity"].ToString();
CSVFILE = productid + "," + quantity;
//Write CSVFILE
//...
}
}
What about such an extension method?
public static IEnumerable<string> JoinEverySecond(this IList<string> list)
{
if (list == null)
{
throw new ArgumentNullException("list");
}
for (var i = 0; i < list.Count; i += 2)
{
if (i + 1 >= list.Count)
{
yield return list[i];
}
yield return string.Join(",", list[i], list[i + 1]);
}
}
Warn: not tested, but I wanted just to present the idea.

Using attributes to find value linq

I’m a LINQ beginner and need some help. Below is the important part of the generated XML generated by a rest query.
<FormData FormOID="F_TABLEOFMODAL_V20" OpenClinica:Version="v2.0" OpenClinica:Status="initial data entry">
<ItemGroupData ItemGroupOID="IG_TABLE_MODALITYTABLE" ItemGroupRepeatKey="3" TransactionType="Insert">
<ItemData ItemOID="I_TABLE_MODAL_DATE_TABLE" Value="2014-04-10" />
<ItemData ItemOID="I_TABLE_MODAL_TYPE_TABLE" Value="4" />
</ItemGroupData>
<ItemGroupData ItemGroupOID="IG_TABLE_MODALITYTABLE" ItemGroupRepeatKey="1" TransactionType="Insert">
<ItemData ItemOID="I_TABLE_MODAL_DATE_TABLE" Value="2014-04-01" />
<ItemData ItemOID="I_TABLE_MODAL_TYPE_TABLE" Value="2" />
</ItemGroupData>
<ItemGroupData ItemGroupOID="IG_TABLE_MODALITYTABLE" ItemGroupRepeatKey="2" TransactionType="Insert">
<ItemData ItemOID="I_TABLE_MODAL_DATE_TABLE" Value="2014-04-04" />
<ItemData ItemOID="I_TABLE_MODAL_TYPE_TABLE" Value="1" />
</ItemGroupData>
</FormData>
What i want to get to are the ItemOIDs. My question is is there some way to get Values for all ItemOID="I_TABLE_MODAL_DATE_TABLE"(ans: two dates) and ItemOID="I_TABLE_MODAL_TYPE_TABLE" (ans: two numbers). The code below works but this uses ItemData for the first element (called date) and then skipping one to second element (type).
////Print out xml to use?
var doc = XDocument.Load(XmlReader.Create(streaming));
XNamespace nsSys = "http://www.cdisc.org/ns/odm/v1.3";
//loop through <ItemGroupData> to get both date and type value
//from each iteration
var items = from i in doc.Descendants(nsSys + "ItemGroupData")
select new
{
date = (string)i.Element(nsSys + "ItemData").Attribute("Value"),
type = (string)i.Descendants(nsSys + "ItemData").Skip(1).First().Attribute("Value")
};
//Catch here?
DataTable modDt = new DataTable();
modDt.Columns.Add(new DataColumn("Date", typeof(string)));
modDt.Columns.Add(new DataColumn("Type", typeof(string))); //changed to string?
//add LINQ-to-XML query result to DataTable
foreach (var item in items)
{
//convert item.date string to DateTime
//then convert it back to string with different format
modDt.Rows.Add(DateTime.ParseExact(item.date, "yyyy-MM-dd", CultureInfo.InvariantCulture)
.ToString("MM/dd/yy"),
int.Parse(item.type));
}
//Value of OC output
//1=mr,2=ct,3=pet,4=us,5=meg,6=ecg (put if statements before xnat rest ssh string)
for (int i = 0; i <= modDt.Rows.Count - 1; i++)
{
modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("1", "mr");
modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("2", "ct");
modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("3", "pet");
modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("4", "us");
modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("5", "meg");
modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("6", "ecg");
}
I've started writting some code shown below. The adding of data into datatable still does not work sorry. However the idea of what i'm trying to do is in there.
DataTable modDt = new DataTable();
modDt.Columns.Add(new DataColumn("Date", typeof(string)));
modDt.Columns.Add(new DataColumn("Type", typeof(string))); //changed to string?
var document = XDocument.Load("doc.xml");
XNamespace nsSys = "http://www.cdisc.org/ns/odm/v1.3";
//Get nodes separated by inner child element
var itd = document.Descendants(nsSys + "ItemData")
.Where(t => (string)t.Attribute("ItemOID") == "I_TABLE_MODAL_DATE_TABLE");
var itds = itd.Attributes("Value").ToArray();
foreach (var it in itds)
{
var subA = it.ToString();
var subAA = subA.ToCharArray();
var subB = subAA.Skip(9).Take(8).ToArray();
string sub = new string(subB);
var subBB = sub.Split('-').ToArray();
string subC = subBB[1] + "/" + subBB[2] + "/" + subBB[0];
foreach (DataRow dr in modDt.Rows)
{
dr["Date"] = subC;
}
Console.WriteLine(subC);
}
var itt = document.Descendants(nsSys + "ItemData")
.Where(t => (string)t.Attribute("ItemOID") == "I_TABLE_MODAL_TYPE_TABLE");
var itty = itt.Attributes("Value").ToArray();
foreach (var et in itty)
{
var subz = et.ToString();
var subzz = subz.ToCharArray();
var subx = subzz.Skip(7).Take(1).ToArray();
string subxx = new string(subx);
foreach (DataRow dt in modDt.Rows)
{
dt["Type"] = subxx;
}
Console.WriteLine(subxx);
}
//for (int i = 0; i <= modDt.Rows.Count - 1; i++)
//{
// modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("1", "mr");
// modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("2", "ct");
// modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("3", "pet");
// modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("4", "us");
// modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("5", "meg");
// modDt.Rows[i][1] = modDt.Rows[i][1].ToString().Replace("6", "ecg");
//}
foreach (DataRow row in modDt.Rows) // Loop over the rows.
{
string sessionDate = row["Date"].ToString();
string mod = row["Type"].ToString();
string drow = "aa" + sessionDate + "bb" + mod + "cc";
Console.WriteLine(drow);
}
Console.Read();
Thank you so much for your help and your time. It is greatly appreciated. Cheers.
Current Attempt
DataTable modDt = new DataTable();
modDt.Columns.Add(new DataColumn("Date", typeof(string)));
modDt.Columns.Add(new DataColumn("Type", typeof(string))); //changed to string?
var document = XDocument.Load("doc.xml");
XNamespace nsSys = "http://www.cdisc.org/ns/odm/v1.3";
//Get nodes separated by inner child element
var values = document.Descendants(nsSys + "ItemData")
.Where(t => t.Attribute("ItemOID").Value == "I_TABLE_MODAL_DATE_TABLE")
.Select(x => x.Attribute("Value").Value);
foreach (var item in values)
{
modDt.Rows.Add(DateTime.ParseExact(item, "yyyy-MM-dd", CultureInfo.InvariantCulture).ToString("MM/dd/yy"),null);
}
var typevalues = document.Descendants(nsSys + "ItemData")
.Where(t => t.Attribute("ItemOID").Value == "I_TABLE_MODAL_TYPE_TABLE")
.Select(x => x.Attribute("Value").Value);
foreach (var item in typevalues)
{
modDt.Rows.Add(null ,int.Parse(item));
}
foreach (DataRow row in modDt.Rows)
{
string sessionDate = row["Date"].ToString();
string mod = row["Type"].ToString();
string xnatCli = mod + sessionDate;
Console.Write(xnatCli);
}
i am trying to answer your question in a single line of code
var values = document.Descendants("ItemData")
.Where(t => t.Attribute("ItemOID").Value == "I_TABLE_MODAL_DATE_TABLE")
.Select(x => x.Attribute("Value").Value)
after this line the values collection will have all the dates. same can be applied to other queries as well.
Update: the solution below use linq to sql, assuming class FormDataContext is prepared for connecting database to relevant table.
FormDataContext db = new FormDataContext();
[Table(Name = "FormData")]
public class FormData
{
[Column]
public DateTime Date;
[Column]
public int Type;
}
IEnumerable<FormData> values = document.Descendants("ItemGroupData").Select(t => {
return new FormData {
FormDate = Convert.ToDateTime(t.XPathSelectElement("ItemData[#ItemOID='I_TABLE_MODAL_DATE_TABLE']").Attribute("Value").Value),
Type = Convert.ToInt32(t.XPathSelectElement("ItemData[#ItemOID='I_TABLE_MODAL_TYPE_TABLE']").Attribute("Value").Value)
};
});
foreach (FormData data in values) {
db.Form.InsertOnSubmit(data);
}
db.SubmitChanges();
This is just an example to store the required two fields to db. You may adjust the example based on your needs.

how i can generate programmatically "insert into" data script file from a database table?

is there an elegant object-orient based framework?
Here is some code that I wrote for generating 'insert' stored procedures for every table in a database. It also handles returning the new id for those tables that have an identity column. It uses SQL SMO. Some of it is a bit specific to my project so please let me know if you have any questions.
void InsertScripts(Database db)
{
var tables = db.Tables.ToIEnumerable(); //this is an extension method to convert Database.Tables into an IEnumerable<Table>
{
foreach (var t in tables)
{
var sb = new StringBuilder();
var sp = new StoredProcedure(db, "gen_insert_" + t.Name);
sp.AnsiNullsStatus = false;
sp.QuotedIdentifierStatus = false;
sp.TextMode = false;
var columns = t.Columns.ToIEnumerable().Where(c => !c.Identity && !c.IsReadOnly()).ToList();
foreach (var c in columns)
{
var p = new StoredProcedureParameter(sp, "#" + t.Name + "_" + c.Name, c.DataType);
p.IsCursorParameter = false;
if(c.Default != null && c.Default.Length > 0)
p.DefaultValue = c.Default;
if (c.Nullable)
p.DefaultValue = "NULL";
sp.Parameters.Add(p);
}
var cols = string.Join(",", columns.Select(c => c.Name).ToArray());
var vals = string.Join(",", columns.Select(c => "#" + t.Name + "_" + c.Name).ToArray());
var sql = string.Format("insert into {0} ({1}) values ({2});", t.Name, cols, vals);
sb.AppendLine(sql);
if (t.Columns.ToIEnumerable().Any(c => c.Identity))
{
var declaration = "declare #newid int;\r\n";
var ret = "select #newid = scope_identity();\r\nselect #newid;\r\nreturn #newid";
sb.Insert(0, declaration);
sb.AppendLine(ret);
}
sp.TextBody = sb.ToString();
if(cols.Length > 0 && sp.Parent.StoredProcedures[sp.Name] == null)
sp.Create();
}
}
}
public static class Utils //Extension methods...
{
public static IEnumerable<Table> ToIEnumerable(this TableCollection tables)
{
var list = new List<Table>();
foreach (Table t in tables)
list.Add(t);
return list;
}
public static IEnumerable<View> ToIEnumerable(this ViewCollection views)
{
var list = new List<View>();
foreach (View v in views)
list.Add(v);
return list;
}
public static IEnumerable<Column> ToIEnumerable(this ColumnCollection columns)
{
var list = new List<Column>();
foreach (Column c in columns)
list.Add(c);
return list;
}
public static IEnumerable<ForeignKey> ToIEnumerable(this ForeignKeyCollection columns)
{
var list = new List<ForeignKey>();
foreach (ForeignKey c in columns)
list.Add(c);
return list;
}
public static IEnumerable<string> ToIEnumerable(this ForeignKeyColumnCollection columns)
{
var list = new List<string>();
foreach (ForeignKeyColumn c in columns)
list.Add(c.Name);
return list;
}
}
It sounds like you want an ORM, or do you actually want the insert text rather than inserting?
You should give Linq To SQL a look.
I just wrote a quick n dirty data export script (for thoose times you can't access the db via SSMS). Anyway, this might help someone in the future:
var result = new StringBuilder();
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString)) {
con.Open();
using (var cmd = con.CreateCommand()) {
cmd.CommandText = #"
DECLARE #name VARCHAR(255)
DECLARE iterator CURSOR FOR SELECT name FROM sys.tables WHERE type='U'
OPEN iterator
FETCH NEXT FROM iterator INTO #name
WHILE ##FETCH_STATUS = 0 BEGIN
SELECT #name name
EXEC ('SELECT * FROM ' + #name)
FETCH NEXT FROM iterator INTO #name
END
CLOSE iterator
DEALLOCATE iterator
";
using (var reader = cmd.ExecuteReader()) {
do {
// get table name
reader.Read();
string tableName = reader[0].ToString();
// get contents
reader.NextResult();
result
.Append("SET IDENTITY_INSERT ")
.Append(tableName)
.Append(" ON\r\n");
while (reader.Read()) {
result
.Append("INSERT ")
.Append(tableName)
.Append(" (");
for (var x = 0; x < reader.FieldCount; x++)
result
.Append(x == 0 ? string.Empty : ",")
.Append("[" + reader.GetName(x) + "]");
result
.Append(" ) VALUES (");
for (var x = 0; x < reader.FieldCount; x++)
result
.Append(x == 0 ? string.Empty : ",")
.Append("'" + reader[x].ToString() + "'");
result
.Append(")\r\n");
}
result
.Append("SET IDENTITY_INSERT ")
.Append(tableName)
.Append(" OFF\r\n");
} while (reader.NextResult());
}
}
}
Response.Write(result);

Categories

Resources