I have created an application where the user can specify queries to databases, maybe even different databases. They would like to have functionality to join the two query results (stored in DataTables) together on user-specified criteria.
The user specifies the join criteria in an XML settings file like this:
<Join Name="join_example" TableAName="tbl_example1" TableBName="tbl_example2" Expression="a.ID == b.ID" />
So far i have converted the DataTables to List of dynamic so the column names are now properties, but I am getting the following error when trying to create the DynamicExpression using those properties:
{"No property or field 'ID' exists in type 'List`1'"}
Any ideas how i can create the dynamic expression? I am open to other ways to perform the join, but would like for the user to be able to use the syntax specified in the XML. Here is my code that is generating the error.
List<dynamic> TableA = ToDynamicList(DataTableA);
List<dynamic> TableB = ToDynamicList(DataTableB);
ParameterExpression paramA = System.Linq.Expressions.Expression.Parameter(TableA.GetType(), "a");
ParameterExpression paramB = System.Linq.Expressions.Expression.Parameter(TableB.GetType(), "b");
Expression Exp = System.Linq.Dynamic.DynamicExpression.Parse(new ParameterExpression[] { paramA, paramB }, TableA.GetType(), this.Expression, new List<dynamic>[] { TableA, TableB });
You don't need to cast the results from tables, you just need to merge them. Here is a linqpad example:
void Main()
{
var dt1 = new DataTable();
dt1.Columns.Add("col1", typeof(string));
dt1.Columns.Add("col2", typeof(int));
var dt2 = new DataTable();
dt2.Columns.Add("col1", typeof(string));
dt2.Columns.Add("col2", typeof(int));
var row = dt1.NewRow();
row["col1"] = "one";
row["col2"] = 1;
dt1.Rows.Add(row);
row = dt1.NewRow();
row["col1"] = "two";
row["col2"] = 2;
dt1.Rows.Add(row);
row = dt2.NewRow();
row["col1"] = "three";
row["col2"] = 3;
dt2.Rows.Add(row);
row = dt2.NewRow();
row["col1"] = "four";
row["col2"] = 4;
dt2.Rows.Add(row);
var dtMerged = dt1.AsEnumerable().CopyToDataTable(); // Note: CopyToDataTable requirs that there are rows. must trap for empty table
dtMerged.Merge(dt2.AsEnumerable().CopyToDataTable(), true, MissingSchemaAction.Add);
dtMerged.Dump();
}
Related
I have defined a dataTable Like this
DataTable dtFinal = new DataTable();
dtFinal.Columns.Add("AVNR", typeof(int));
dtFinal.Columns.Add("Substation", typeof(string));
dtFinal.Columns.Add("ColumnTitle", typeof(string));
dtFinal.Columns.Add("S6_NAME", typeof(string));
dtFinal.Columns.Add("Voltage", typeof(string));
dtFinal.Columns.Add("Wert", typeof(decimal));
and I make a join between two tables to have a result set
var results = from table1 in dtTimeListTable.AsEnumerable()
join table2 in readyDataTable.AsEnumerable() on (decimal)table1["Avnr"] equals (int)table2["Avnr"]
select new
{
AVNR = (int)table2["AVNR"],
Substation = (string)table2["Substation"],
ColumnTitle = (string)table2["ColumnTitle"],
S6_NAME = (string)table2["S6_NAME"],
Voltage = (string)table2["Voltage"],
Wert = (decimal)table1["Wert"]
};
to fill datatable up I do the following:
dtFinal.Rows.Add(results.ToArray());
but I'll get a error liek this
input array is longer than the number of columns in this table
both datatable have 6 columns, what could be the problem?
DataRowCollection.Add is a method to add a single DataRow but you are trying to add all rows.
You need a loop:
foreach(var x in query)
dtFinal.Rows.Add(x.AVNR, x.Substation, x.ColumnTitle, x.S6_NAME, x.Voltage, x.Wert);
You could build the object[] for each DataRow also in this way:
var joinedRows = from table1 in dtTimeListTable.AsEnumerable()
join table2 in readyDataTable.AsEnumerable() on (decimal) table1["Avnr"] equals (int) table2["Avnr"]
select new { r1 = table1, r2 = table2 };
foreach (var x in joinedRows)
{
object[] fields =
{
x.r2.Field<int>("AVNR"), x.r2.Field<string>("Substation"), x.r2.Field<string>("ColumnTitle"),
x.r2.Field<int>("S6_NAME"), x.r2.Field<string>("Voltage"), x.r1.Field<decimal>("Wert"),
};
dtFinal.Rows.Add(fields);
}
I get the following error when trying to add a datacolumn to a datatable: Cannot add a nested relation or an element column to a table containing a SimpleContent column. This happens when I hit this code the first time mt.Columns.Add("IdentityId", typeof(int));
The odd thing is that, when I view the datatable in the debugger after the previous error, the column is there. When I hit to continue, the error is: A column named 'IdentityId' already belongs to this DataTable. It seems that it adds it, and then generates the error.
Here's the code:
XElement doc = XElement.Load(XmlFileName);
string TextToFind = "Some Text";
IEnumerable<XElement> query1 = doc.Descendants("desc").Where(c => c.Value == TextToFind).Ancestors("re");
IEnumerable<XElement> query2 = query1.First().Parent.ElementsAfterSelf("ti");
string xml = query2.First().ToString();
stream = new StringReader(xml);
reader = new XmlTextReader(stream);
xmlDataset.ReadXml(reader);
DataTable mt = xmlDataset.Tables["mt"];
DataColumn dc = mt.Columns.Add("IdentityId", typeof(int));
dc.AutoIncrement = true;
dc.AutoIncrementSeed = 1;
dc.AutoIncrementStep = 1;
I know it has something to do with the fact that the datatable comes from XML, and the few solutions I have found deal more with the xml than with the datatable. The XML cannot be changed.
Since there was no help, I ended up copying with traditional loops. I tried to use Copy and Close, but I kept receiving the same error. MSDN was able to guide me through a solution:
private DataTable ConvertToRegularDataTable(DataTable source)
{
DataTable result = new DataTable();
foreach (DataColumn c in source.Columns)
{
result.Columns.Add(c.ColumnName, typeof(string));
}
DataColumn dc = new DataColumn("ID"); // table.Columns.Add("IdentityId", typeof(int));
dc.AutoIncrement = true;
dc.AutoIncrementSeed = 1;
dc.AutoIncrementStep = 1;
result.Columns.Add(dc);
foreach (DataRow dr in source.Rows)
{
result.Rows.Add(dr.ItemArray);
}
return result;
}
I have the following LINQ query :
var groupedData = from b in loans.AsEnumerable()
group b by b.Field<int>("loan_code") & b.Field<int>("emp_num")
into f
select f.CopyToDataTable();
I want to select f and in addition to that the summation of Tot field and copy the result in data table .how to do that?
Get required data
var groupedData = from r in loans.AsEnumerable()
group r by new {
LoanCode = r.Field<int>("loan_code"),
EmpNum = r.Field<int>("emp_num")
} into g
select new {
g.Key.LoanCode,
g.Key.EmpNum,
Tot = g.Sum(r => r.Field<int>("Tot")) // assume integer
};
Then use custom CopyToDataTable method (which works for types that don't implement DataRow) to convert them to DataTable. Or you can build DataTable manually:
DataTable dt = new DataTable();
dt.Columns.Add("loan_code", typeof(int));
dt.Columns.Add("emp_num", typeof(int));
dt.Columns.Add("Tot", typeof(int));
foreach(var data in groupedData)
dt.Rows.Add(data.LoanCode, data.EmpNum, data.Tot);
is there any way to replace all occurrences of a value in a data table with another value from a different data table.for example I have two data table one has Itemid and another has itemid and item name.I need to replace item id in first data table with the item name from the second data table..Is there any possible way to replace all occurances at one go or should i go for the usual loop method and use Datatable.Select method.Please help.Thanks in advance.
What about this? (I realise it is a loop - but very compact and any built in method would loop anyway, just behind the scenes)
//Build first Test DT
DataTable dt1 = new DataTable();
dt1.Columns.Add("itemID", typeof(string));
//Build Second Test DT
DataTable dt2 = new DataTable();
dt2.Columns.Add("itemID", typeof(string));
dt2.Columns.Add("itemName", typeof(string));
//aad 3 DataRows to first DT - ID only
DataRow dt1_1 = dt1.NewRow();
dt1_1["itemID"] = "1";
DataRow dt1_2 = dt1.NewRow();
dt1_2["itemID"] = "2";
DataRow dt1_3 = dt1.NewRow();
dt1_3["itemID"] = "3";
dt1.Rows.Add(dt1_1);
dt1.Rows.Add(dt1_2);
dt1.Rows.Add(dt1_3);
//aad 3 DataRows to first DT - ID & Name
DataRow dt2_1 = dt2.NewRow();
dt2_1["itemID"] = "1";
dt2_1["itemName"] = "ItemOne";
DataRow dt2_2 = dt2.NewRow();
dt2_2["itemID"] = "2";
dt2_2["itemName"] = "ItemTwo";
DataRow dt2_3 = dt2.NewRow();
dt2_3["itemID"] = "3";
dt2_3["itemName"] = "ItemThree";
dt2.Rows.Add(dt2_1);
dt2.Rows.Add(dt2_2);
dt2.Rows.Add(dt2_3);
////////////////////////////////////////////////////////
//replacing code - quite comact - assumed itemId is PK//
////////////////////////////////////////////////////////
foreach (DataRow dr in dt1.Rows)
{
string strSelect = "[itemID] = '"+ dr["itemID"] +"'";
DataRow[] myRow = dt2.Select(strSelect);
if (myRow.Length == 1)
{
dr["itemID"] = myRow[0]["itemName"];
}
}
/////////////////////////////////////////////////////////////////
//dt1 now has itemOne, itemTwo and itemThree instead of 1, 2, 3//
/////////////////////////////////////////////////////////////////
With MySQL I would try:
UPDATE table1 t1, table2 t2
SET t1.itemid = t2.itemname
WHERE t1.itemid = t2.itemid
With MS-SQL I would try
UPDATE t1
SET t1.itemid = t2.itemname
FROM table1 t1 INNER JOIN table2 t2
ON t1.itemid = t2.itemid
I am having two datatables and my basic need is to know whether the value in a datatable is exist on the another datatable.
My first datatable will contains data like this (id,name,unit)
Second one like this (id,value).
The values may be like this
1-A-b,2-B-c,3-X-d for first one
and 1-2,3,5 for second one. Here 1 and 3 are exsting.How can I find the corresponding values using the id.
this should work;
var table = new DataTable();
table.Columns.Add("id", typeof(int));
table.Columns.Add("name");
table.Columns.Add("unit");
var table2 = new DataTable();
table2.Columns.Add("id", typeof(int));
table2.Columns.Add("value");
table.Rows.Add(1, "a Name", "a Unit");
table.Rows.Add(2, "other", "other");
table2.Rows.Add(1, "value");
table2.Rows.Add(4, "other");
var result = table.AsEnumerable().Join(table2.AsEnumerable(), r1 => r1.Field<int>("id"), r2 => r2.Field<int>("id"),
(r1, r2) => new {Id = r1.Field<int>("id"), Value = r2.Field<string>("value") }).ToList();
foreach (var r in result)
Console.WriteLine(r.Id + "|"+ r.Value);