Adding Sum per row in PIVOT using LINQ - linq

I have following function that converts normal DataTable to Pivot Table.
But I have 1 more requirement, it should also show Value column i.e sum of all the Pivot column cell.
e.g. : ORDER | SUM | PLANDATE
1 50 10-Oct-2012
1 10 10-Oct-2012
2 15 12-Oct-2012
After PIVOT it should be like
ORDER | SUM | 10-Oct-2012 | 12-Oct-2012
1 60 50 10
2 15 15
my pivot function is :
DataTable Pivot(DataTable dt, DataColumn pivotColumn, DataColumn pivotValue) {
// find primary key columns
//(i.e. everything but pivot column and pivot value)
DataTable temp = dt.Copy();
temp.Columns.Remove( pivotColumn.ColumnName );
temp.Columns.Remove( pivotValue.ColumnName );
string[] pkColumnNames = temp.Columns.Cast<DataColumn>()
.Select( c => c.ColumnName )
.ToArray();
// prep results table
DataTable result = temp.DefaultView.ToTable(true, pkColumnNames).Copy();
result.PrimaryKey = result.Columns.Cast<DataColumn>().ToArray();
dt.AsEnumerable()
.Select(r => r[pivotColumn.ColumnName].ToString())
.Distinct().ToList()
.ForEach (c => result.Columns.Add(c, pivotColumn.DataType));
// load it
foreach( DataRow row in dt.Rows ) {
// find row to update
DataRow aggRow = result.Rows.Find(
pkColumnNames
.Select( c => row[c] )
.ToArray() );
// the aggregate used here is LATEST
// adjust the next line if you want (SUM, MAX, etc...)
aggRow[row[pivotColumn.ColumnName].ToString()] = row[pivotValue.ColumnName];
}
return result;
}
Thanks & any help will be appriciated...
Sample Data :
OrderStatus Qty Dated
New 100 10-Oct-2012
New 150 10-Oct-2012
New 100 10-Oct-2012
Conf 200 12-Oct-2012
New 100 12-Oct-2012
Reconf 300 12-Oct-2012
New 200 15-Oct-2012
New 200 15-Oct-2012
Reconf 100 18-Oct-2012
New 150 18-Oct-2012
Conf 150 18-Oct-2012
New 100 20-Oct-2012
Reconf 500 20-Oct-2012
New 100 30-Oct-2012
New 700 30-Oct-2012
New 100 30-Oct-2012
Conf 100 10-Oct-2012
Update Code :
private DataTable DataTableToPivot(DataTable dtNormalTable, DataColumn pivotColumn, DataColumn valueColumn)
{
cTracing.WriteTrace("Inside DataTableToPivot", "DataTableToPivot");
DataTable dtPivot = null;
DataTable tempPivot = null;
DataColumn sumColumn = new DataColumn();
string[] pkColumnNames = null;
try
{
sumColumn.ColumnName = valueColumn.ColumnName;
sumColumn.DataType = typeof(string);
sumColumn.DefaultValue = "0";
var allrows = dt.AsEnumerable().Select(delegate(DataRow r)
{
DataRow nr = dt.NewRow();
foreach (DataColumn item in dt.Columns)
{
nr.SetField(item.ColumnName, r[item.ColumnName] is DBNull ? string.Empty : r[item.ColumnName]);
}
return nr;
});
dtNormalTable = allrows.CopyToDataTable();
// find primary key columns
//(i.e. everything but pivot column and pivot value)
tempPivot = dtNormalTable.Copy();
tempPivot.Columns.Remove(pivotColumn.ColumnName);
tempPivot.Columns.Remove(valueColumn.ColumnName);
pkColumnNames = tempPivot.Columns
.Cast<DataColumn>()
.Select(c => c.ColumnName)
.ToArray();
// prep results table
dtPivot = tempPivot.DefaultView.ToTable(true, pkColumnNames).Copy();
dtPivot.PrimaryKey = dtPivot.Columns.Cast<DataColumn>().ToArray();
// include the sum column
dtPivot.Columns.Add(sumColumn);
try
{
dtNormalTable.AsEnumerable()
.Select(r => r[pivotColumn.ColumnName].ToString())
.Distinct()
.ToList()
.ForEach(c => dtPivot.Columns.Add(Convert.ToDateTime(c).ToString("dd-MMM-yyyy"), typeof(string)));
}
catch
{
dtNormalTable.AsEnumerable()
.Select(r => r[pivotColumn.ColumnName].ToString())
.Distinct()
.ToList()
.ForEach(c => dtPivot.Columns.Add(c, typeof(string)));
}
// load it
foreach (DataRow row in dtNormalTable.Rows)
{
// find row to update
DataRow aggRow = dtPivot.Rows.Find(
pkColumnNames
.Select(c => row[c])
.ToArray());
// the aggregate used here is LATEST
// adjust the next line if you want (SUM, MAX, etc...)
string columnName = string.Empty;
try
{
columnName = Convert.ToDateTime(row[pivotColumn.ColumnName]).ToString("dd-MMM-yyyy");
}
catch
{
columnName = row[pivotColumn.ColumnName].ToString();
}
if (aggRow.IsNull(columnName))
{
aggRow[columnName] = Convert.ToDecimal(row[valueColumn.ColumnName]);
}
else
{
aggRow[columnName] = Convert.ToDecimal(aggRow[columnName]) +
Convert.ToDecimal(row[valueColumn.ColumnName]);
}
if (numberFormat == "3")
{
// add the value to the sum
aggRow[sumColumn] = (Convert.ToDecimal(aggRow[sumColumn].ToString()) +
Convert.ToDecimal(row[valueColumn.ColumnName].ToString())).ToString("#0.000");
}
else if (numberFormat == "2")
{
// add the value to the sum
aggRow[sumColumn] = (Convert.ToDecimal(aggRow[sumColumn].ToString()) +
Convert.ToDecimal(row[valueColumn.ColumnName].ToString())).ToString("#0.00");
}
else if (numberFormat == "1")
{
// add the value to the sum
aggRow[sumColumn] = (Convert.ToDecimal(aggRow[sumColumn].ToString()) +
Convert.ToDecimal(row[valueColumn.ColumnName].ToString())).ToString("#0.0");
}
else
{
// add the value to the sum
aggRow[sumColumn] = (Convert.ToDecimal(aggRow[sumColumn].ToString()) +
Convert.ToDecimal(row[valueColumn.ColumnName].ToString())).ToString("#0");
}
}
return dtPivot;
}
catch (Exception ex)
{
cTracing.WriteTrace("Error inside DataTableToPivot", "DataTableToPivot");
cLogging.WriteLog(ex);
return null;
}
finally
{
if (dtPivot != null)
dtPivot.Dispose();
}
}
enter code here

How about adding each row's value to the sum column in the corresponding aggregate row?
DataTable Pivot(DataTable dt, DataColumn pivotColumn, DataColumn pivotValue) {
DataColumn sumColumn = new DataColumn();
sumColumn.ColumnName = "SUM";
sumColumn.DataType = typeof(int);
sumColumn.DefaultValue = 0;
// find primary key columns
//(i.e. everything but pivot column and pivot value)
DataTable temp = dt.Copy();
temp.Columns.Remove( pivotColumn.ColumnName );
temp.Columns.Remove( pivotValue.ColumnName );
string[] pkColumnNames = temp.Columns.Cast<DataColumn>()
.Select( c => c.ColumnName )
.ToArray();
// prep results table
DataTable result = temp.DefaultView.ToTable(true, pkColumnNames).Copy();
result.PrimaryKey = result.Columns.Cast<DataColumn>().ToArray();
// include the sum column
result.Columns.Add(sumColumn);
dt.AsEnumerable()
.Select(r => r[pivotColumn.ColumnName].ToString())
.Distinct()
.OrderBy(c => Convert.ToDateTime(c)) // Order by the date
.ToList()
.ForEach (c => result.Columns.Add(c, pivotColumn.DataType));
// load it
foreach( DataRow row in dt.Rows ) {
// find row to update
DataRow aggRow = result.Rows.Find(
pkColumnNames
.Select( c => row[c] )
.ToArray() );
// the aggregate used here is LATEST
// adjust the next line if you want (SUM, MAX, etc...)
string columnName = row[pivotColumn.ColumnName].ToString();
if(aggRow.IsNull(columnName))
{
aggRow[columnName] = (int)row[pivotValue.ColumnName];
}
else
{
aggRow[columnName] = Convert.ToInt32(aggRow[columnName]) +
(int)row[pivotValue.ColumnName];
}
// add the value to the sum
aggRow[sumColumn] = (int)aggRow[sumColumn] +
(int)row[pivotValue.ColumnName];
}
return result;
}

Related

Dynamic LINQ Where query with joining other tables

I have several tables (the exact number is not known when the program is build) looking like this (the number of rows and columns may differ from table to table):
The source data is stored in a data set. Now I want to generate a new table where all data of all ids is stored (the picture shows only the result for id 10 and 20 but the target table should contain the data for all ids):
The equivalent SQLite statement for that looks like this:
SELECT * FROM Dataset
JOIN Datensatz2 ON (Dataset.ID=Datensatz2.ID)
JOIN Datensatz3 ON (Datensatz3.ID=Dataset.ID)
JOIN Datensatz4 ON (Datensatz4.ID=Dataset.ID)
WHERE Dataset.Id=10
UNION
SELECT * FROM Dataset
JOIN Datensatz2 ON (Dataset.ID=Datensatz2.ID)
JOIN Datensatz3 ON (Datensatz3.ID=Dataset.ID)
JOIN Datensatz4 ON (Datensatz4.ID=Dataset.ID)
WHERE Dataset.Id=20
...
The double id columns will be removed afterwards so donĀ“t worry about that. The questions is now how to convert it as a dynamic LINQ query?
There are plenty of open question but maybe this helps to solve it. Since the tables are already stored in a DataSet you could use Linq-To-DataSet and Enumerable.GroupBy to group by ID:
var idTables = ds.Tables.Cast<DataTable>().Where(t => t.Columns.Contains("Id"));
if(!idTables.Any()){ MessageBox.Show("No id-tables"); return; }
var idRowGroups = idTables.SelectMany(t => t.AsEnumerable())
.GroupBy(row => row.Field<int>("Id"))
.Select(grp => new { ID = grp.Key, Rows = grp });
foreach(var idGroup in idRowGroups)
{
Console.WriteLine("ID:{0} Rows:{1}"
, idGroup.ID
, String.Join(" | ", idGroup.Rows.Select(row => String.Join(",", row.ItemArray))));
}
Sample data:
var ds = new DataSet();
DataTable t1 = new DataTable();
t1.Columns.Add("Id", typeof(int));
t1.Columns.Add("Data", typeof(int));
t1.Rows.Add(1, 1);
t1.Rows.Add(2, 10);
t1.Rows.Add(3, 100);
t1.Rows.Add(4, 1000);
ds.Tables.Add(t1);
t1 = new DataTable();
t1.Columns.Add("Id", typeof(int));
t1.Columns.Add("Data", typeof(int));
t1.Rows.Add(4, 5);
t1.Rows.Add(5, 50);
t1.Rows.Add(7, 500);
t1.Rows.Add(3, 5997);
ds.Tables.Add(t1);
t1 = new DataTable();
t1.Columns.Add("Id", typeof(int));
t1.Columns.Add("Data1", typeof(int));
t1.Columns.Add("Data2", typeof(int));
t1.Rows.Add(1, 5, 0);
t1.Rows.Add(3, 7, 1);
t1.Rows.Add(5, 9, 11);
t1.Rows.Add(7, 11, 222);
ds.Tables.Add(t1);
Output:
ID:1 Rows:1,1 | 1,5,0
ID:2 Rows:2,10
ID:3 Rows:3,100 | 3,5997 | 3,7,1
ID:4 Rows:4,1000 | 4,5
ID:5 Rows:5,50 | 5,9,11
ID:7 Rows:7,500 | 7,11,222
Ok, I finally made it but it seems to be much too complicated. If someone is able to help me improve the solution he is very welcome.
DataSet dsResult = new DataSet();
var idTables = ds.Tables.Cast<DataTable>().Where(t => t.Columns.Contains("ID"));
if (!idTables.Any()) { MessageBox.Show("No id-tables"); return; }
var idRowGroups = idTables.SelectMany(t => t.AsEnumerable())
.GroupBy(row => row.Field<Int64>("ID"))
.Select(grp => new { ID = grp.Key, Rows = grp });
foreach (var idGroup in idRowGroups)
{
var liste = idGroup.Rows.ToList();
for (int i = 0; i < liste.Count; i++)
{
if (!dsResult.Tables.Contains(liste[i].Table.TableName))
{
dsResult.Tables.Add(liste[i].Table.TableName);
foreach (DataColumn dtCol in liste[i].Table.Columns)
{
if (dsResult.Tables[liste[i].Table.TableName].Columns.Contains("ID"))
dsResult.Tables[liste[i].Table.TableName].Columns.Add(dtCol.ColumnName+i.ToString());
else
{
dsResult.Tables[liste[i].Table.TableName].Columns.Add(dtCol.ColumnName);
}
dsResult.Tables[liste[i].Table.TableName].Columns[dtCol.ColumnName].DataType = dtCol.DataType;
}
}
DataRow dRow = dsResult.Tables[liste[i].Table.TableName].NewRow();
dRow.ItemArray = liste[i].ItemArray;
dsResult.Tables[liste[i].Table.TableName].Rows.Add(dRow);
}
IEnumerable<IEnumerable<DataRow>> allTablesRows = dsResult.Tables.Cast<DataTable>()
.Select(table => table.AsEnumerable())
.CartesianProduct();
int k = 0;
foreach (var rows in allTablesRows)
{
DataRow zRow = dsErgebnis.Tables[2].NewRow();
foreach (DataRow dRow in rows)
{
for (int i = 0; i < dRow.ItemArray.Length; i++)
{
zRow[k] = dRow.ItemArray[i];
k++;
}
}
k = 0;
dsErgebnis.Tables[2].Rows.Add(zRow);
}
dsResult.Clear();
}
First I filter the content by ID.
Then I put the result in a new tables (all rows with the ID 10 I found in 'Datasatz2' in dataset 'ds' for example I put to a new table 'Datasatz2' in the dataset 'dsResult').
At least I build the cartesian product of all tables and store it in dtaset dsErgebnis.
The result is what I expect but as mentioned before I am not satisfied with the solution.

Dynamic PIVOT using C# Linq

I am trying to use following code to create the PIVOT but its not working.
It's giving me compile time error. I don't know linq so unable to use it.
Please help :
DataTable Pivot(DataTable dt, DataColumn pivotColumn, DataColumn pivotValue) {
// find primary key columns
//(i.e. everything but pivot column and pivot value)
DataTable temp = dt.Copy();
temp.Columns.Remove( pivotColumn.ColumnName );
temp.Columns.Remove( pivotValue.ColumnName );
string[] pkColumnNames = temp.Columns.Cast(<DataColumn>)
.Select( c => c.ColumnName )
.ToArray();
// prep results table
DataTable result = temp.DefaultView.ToTable(true, pkColumnNames).Copy();
result.PrimaryKey = result.Columns.Cast(<DataColumn>).ToArray();
dt.AsEnumerable()
.Select(r =>; r[pivotColumn.ColumnName].ToString())
.Distinct().ToList()
.ForEach (c => result.Columns.Add(c, pivotColumn.DataType));
// load it
foreach( DataRow row in dt.Rows ) {
// find row to update
DataRow aggRow = result.Rows.Find(
pkColumnNames
.Select( c => row[c] )
.ToArray() );
// the aggregate used here is LATEST
// adjust the next line if you want (SUM, MAX, etc...)
aggRow[row[pivotColumn.ColumnName].ToString()] = row[pivotValue.ColumnName];
}
return result;
}
Code from : http://michaeljswart.com/2011/06/forget-about-pivot/
Moreover it tried to use following code, it works well except for it is not giving total sum for Value Column
public DataTable GetInversedDataTable(DataTable table, string columnX, string columnY, string columnZ, string nullValue, bool sumValues)
{
//Create a DataTable to Return
DataTable returnTable = new DataTable();
DataTable tempTable = table.Clone();
if (string.IsNullOrEmpty(columnX))
{
columnX = table.Columns[0].ColumnName;
}
tempTable.Columns.Remove(columnX);
//Add a Column at the beginning of the table
//returnTable.Columns.Add(columnY);
returnTable = tempTable.Clone();
//Read all DISTINCT values from columnX Column in the provided DataTale
List<string> columnXValues = new List<string>();
foreach (DataRow dr in table.Rows)
{
string columnXTemp = dr[columnX].ToString();
if (!columnXValues.Contains(columnXTemp))
{
//Read each row value, if it's different from others provided, add to the list of values and creates a new Column with its value.
columnXValues.Add(columnXTemp);
returnTable.Columns.Add(columnXTemp);
}
}
//Verify if Y and Z Axis columns re provided
if (!string.IsNullOrEmpty(columnY) && !string.IsNullOrEmpty(columnZ))
{
//Read DISTINCT Values for Y Axis Column
List<string> columnYValues = new List<string>();
foreach (DataRow dr in table.Rows)
{
if (!columnYValues.Contains(dr[columnY].ToString()))
{
columnYValues.Add(dr[columnY].ToString());
}
}
//Loop all Column Y Distinct Value
foreach (string columnYValue in columnYValues)
{
//Creates a new Row
DataRow drReturn = returnTable.NewRow();
drReturn[0] = columnYValue;
//foreach column Y value, The rows are selected distincted
DataRow[] rows = table.Select((columnY + "='") + columnYValue + "'");
//Read each row to fill the DataTable
foreach (DataRow dr in rows)
{
string rowColumnTitle = dr[columnX].ToString();
//Read each column to fill the DataTable
foreach (DataColumn dc in returnTable.Columns)
{
if (dc.ColumnName == rowColumnTitle)
{
//If Sum of Values is True it try to perform a Sum
//If sum is not possible due to value types, the value displayed is the last one read
if (sumValues)
{
try
{
drReturn[rowColumnTitle] = Convert.ToDecimal(drReturn[rowColumnTitle]) + Convert.ToDecimal(dr[columnZ]);
}
catch
{
drReturn[rowColumnTitle] = dr[columnZ];
}
}
else
{
drReturn[rowColumnTitle] = dr[columnZ];
}
}
}
}
returnTable.Rows.Add(drReturn);
}
}
else
{
throw new Exception("The columns to perform inversion are not provided");
}
//if a nullValue is provided, fill the datable with it
if (!string.IsNullOrEmpty(nullValue))
{
foreach (DataRow dr in returnTable.Rows)
{
foreach (DataColumn dc in returnTable.Columns)
{
if (string.IsNullOrEmpty(dr[dc.ColumnName].ToString()))
{
dr[dc.ColumnName] = nullValue;
}
}
}
}
return returnTable;
}
GetInversedDataTable(dtNormal, "Dated", "OrderStatus", "Qty", " ", true);
Please help :)
Here is the code with the compilation errors corrected:
DataTable Pivot(DataTable dt, DataColumn pivotColumn, DataColumn pivotValue) {
// find primary key columns
//(i.e. everything but pivot column and pivot value)
DataTable temp = dt.Copy();
temp.Columns.Remove( pivotColumn.ColumnName );
temp.Columns.Remove( pivotValue.ColumnName );
string[] pkColumnNames = temp.Columns.Cast<DataColumn>()
.Select( c => c.ColumnName )
.ToArray();
// prep results table
DataTable result = temp.DefaultView.ToTable(true, pkColumnNames).Copy();
result.PrimaryKey = result.Columns.Cast<DataColumn>().ToArray();
dt.AsEnumerable()
.Select(r => r[pivotColumn.ColumnName].ToString())
.Distinct().ToList()
.ForEach (c => result.Columns.Add(c, pivotColumn.DataType));
// load it
foreach( DataRow row in dt.Rows ) {
// find row to update
DataRow aggRow = result.Rows.Find(
pkColumnNames
.Select( c => row[c] )
.ToArray() );
// the aggregate used here is LATEST
// adjust the next line if you want (SUM, MAX, etc...)
aggRow[row[pivotColumn.ColumnName].ToString()] = row[pivotValue.ColumnName];
}
return result;
}
I changed Cast(<DataColumn>) to Cast<DataColumn>() in two locations and got rid of the semicolon in the middle of a lambda expression. The second part of your question is a little trickier. You may want to ask it as its own question.
Good one., but you might want to replace the below line
.ForEach (c => result.Columns.Add(c, pivotColumn.DataType));
with this (change pivotColumn to pivotValue)
.ForEach (c => result.Columns.Add(c, pivotValue.DataType));
Works perfectly for my requirement.

Optimize queries for Union, Except, Join with LINQ and C#

I have 2 objects (lists loaded from XML) report and database (showed bellow in code) and i should analyse them and mark items with 0, 1, 2, 3 according to some conditions
TransactionResultCode = 0; // SUCCESS (all fields are equivalents: [Id, AccountNumber, Date, Amount])
TransactionResultCode = 1; // Exists in report but Not in database
TransactionResultCode = 2; // Exists in database but Not in report
TransactionResultCode = 3; // Field [Id] are equals but other fields [AccountNumber, Date, Amount] are different.
I'll be happy if somebody could found time to suggest how to optimize some queries.
Bellow is the code:
THANK YOU!!!
//TransactionResultCode = 0 - SUCCESS
//JOIN on all fields
var result0 = from d in database
from r in report
where (d.TransactionId == r.MovementID) &&
(d.TransactionAccountNumber == long.Parse(r.AccountNumber)) &&
(d.TransactionDate == r.MovementDate) &&
(d.TransactionAmount == r.Amount)
orderby d.TransactionId
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount,
TransactionResultCode = 0
};
//*******************************************
//JOIN on [Id] field
var joinedList = from d in database
from r in report
where d.TransactionId == r.MovementID
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount
};
//Difference report - database
var onlyReportID = report.Select(r => r.MovementID).Except(joinedList.Select(d => d.TransactionId));
//TransactionResultCode = 1 - Not Found in database
var result1 = from o in onlyReportID
from r in report
where (o == r.MovementID)
orderby r.MovementID
select new TransactionList()
{
TransactionId = r.MovementID,
TransactionAccountNumber = long.Parse(r.AccountNumber),
TransactionDate = r.MovementDate,
TransactionAmount = r.Amount,
TransactionResultCode = 1
};
//*******************************************
//Difference database - report
var onlyDatabaseID = database.Select(d => d.TransactionId).Except(joinedList.Select(d => d.TransactionId));
//TransactionResultCode = 2 - Not Found in report
var result2 = from o in onlyDatabaseID
from d in database
where (o == d.TransactionId)
orderby d.TransactionId
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount,
TransactionResultCode = 2
};
//*******************************************
var qwe = joinedList.Select(j => j.TransactionId).Except(result0.Select(r => r.TransactionId));
//TransactionResultCode = 3 - Transaction Results are different (Amount, AccountNumber, Date, )
var result3 = from j in joinedList
from q in qwe
where j.TransactionId == q
select new TransactionList()
{
TransactionId = j.TransactionId,
TransactionAccountNumber = j.TransactionAccountNumber,
TransactionDate = j.TransactionDate,
TransactionAmount = j.TransactionAmount,
TransactionResultCode = 3
};
you may try something like below:
public void Test()
{
var report = new[] {new Item(1, "foo", "boo"), new Item(2, "foo2", "boo2"), new Item(3, "foo3", "boo3")};
var dataBase = new[] {new Item(1, "foo", "boo"), new Item(2, "foo22", "boo2"), new Item(4, "txt", "rt")};
Func<Item, bool> inBothLists = (i) => report.Contains(i) && dataBase.Contains(i);
Func<IEnumerable<Item>, Item, bool> containsWithID = (e, i) => e.Select(_ => _.ID).Contains(i.ID);
Func<Item, int> getCode = i =>
{
if (inBothLists(i))
{
return 0;
}
if(containsWithID(report, i) && containsWithID(dataBase, i))
{
return 3;
}
if (report.Contains(i))
{
return 2;
}
else return 1;
};
var result = (from item in dataBase.Union(report) select new {Code = getCode(item), Item = item}).Distinct();
}
public class Item
{
// You need also to override Equals() and GetHashCode().. I omitted them to save space
public Item(int id, string text1, string text2)
{
ID = id;
Text1 = text1;
Text2 = text2;
}
public int ID { get; set; }
public string Text1 { get; set; }
public string Text2 { get; set; }
}
Note that you need to either implement Equals() for you items, or implement an IEqualityComparer<> and feed it to Contains() methods.

Entity Framework ObjectContext.SaveChanges fails on unique key column update

Consider very simple database table:
CREATE TABLE UkTest(
id int NOT NULL,
uk int NOT NULL
)
Primary Key on id
Unique Key on uk
Then add 2 rows:
INSERT INTO UkTest (id,uk) VALUES(1,1);
INSERT INTO UkTest (id,uk) VALUES(2,2);
Then do 2 tests.
OK:
var db = new Database1Entities();
var element1 = db.UkTest.FirstOrDefault(e => e.id == 1);
var element2 = db.UkTest.FirstOrDefault(e => e.id == 2);
element1.uk = 0;
element2.uk = 1; // overrides previous element1.uk value
var count = db.SaveChanges();
Fails (before test revert uk values to 1 and 2!):
var db = new Database1Entities();
var element1 = db.UkTest.FirstOrDefault(e => e.id == 1);
var element2 = db.UkTest.FirstOrDefault(e => e.id == 2);
element2.uk = 0;
element1.uk = 2; // overrides previous element2.uk value
var count = db.SaveChanges();
// Cannot insert duplicate key row in object 'dbo.UkTest' with unique index 'UK_UkTest'
See that ObjectContext.SaveChanges() checks rows in the order of primary index.
Is there any way to force own order?
Unless you call SaveChanges() twice, no, there is no way to control the order of SQL statements Entity Framework will send to the database. You could wrap the multiple SaveChanges() calls into an outer transaction to ensure still a transactional behaviour for the whole operation:
using (var scope = new TransactionScope())
{
using (var db = new Database1Entities())
{
var element1 = db.UkTest.FirstOrDefault(e => e.id == 1);
var element2 = db.UkTest.FirstOrDefault(e => e.id == 2);
element2.uk = 0;
db.SaveChanges();
element1.uk = 2;
db.SaveChanges();
}
scope.Complete();
}
Thanks to Slauma. I've found the solution. The key is to keep elements in several ObjectContext instances.
public class SavingElementsWithTransactionInOwnOrder
{
public void SaveElements ()
{
var db = new Database1Entities();
var element1 = db.UkTest.FirstOrDefault(e => e.id == 1);
element1.db = db;
db = new Database1Entities();
var element2 = db.UkTest.FirstOrDefault(e => e.id == 2);
element2.db = db;
element2.uk = 0;
element1.uk = 2;
var scope = new TransactionScope();
try{
element2.db.SaveChanges();
element1.db.SaveChanges();
scope.Complete();
}
finally{
scope.Dispose();
}
}
}
public partial class UkTest
{
public Database1Entities db { get; set; }
}

DataTable linq query iteration

I want to Enumerate Linq Query. Below i specified example.
EX:
DataTable _slidingDataTable = new DataTable("test");
for(int i=0; i<5;i++)
{
DataRow row = _slidingDataTable.NewRow();
startPosition = DateTime.Now;
for(int i=0; i<5;i++)
{
_slidingDataTable.Columns.Add("TransferTime");
row[columnName] = startPosition ;
_slidingDataTable.Columns.Add("TransferData");
row[columnName] = "Test"+i;
}
_slidingDataTable.Rows.Add(row);
}
var query1 = from myRow in _slidingDataTable.AsEnumerable()
where myRow.Field<DateTime>("TransferTime") == startPosition
select myRow;
This query output should be collection of rows. How to get collection row & iterate.
In your context, query1 is an EnumerableRowCollection<DataRow> because you used _slidingDataTable.AsEnumerable(), and you can iterate over it like so :
foreach (DataRow row in query1)
{
// Do stuff with that row
}
I'm giving you an example by which you can see it and it also includes sum in groupby.
var drdatedisp = from row in dtfullreport.AsEnumerable()
group row by row.Field<string>("Order_Date") into g
select new
{
Order_Date = g.Key,
totalQnty = g.Sum(a => a.Field<int>("Item_Quantity")),
totalTax = g.Sum(a => float.Parse(a.Field<decimal>("TAXAMT").ToString())),
totalAmt = g.Sum(a => float.Parse(a.Field<decimal>("VALAMT").ToString()))
};
DataTable dtdatedisp = new DataTable();
dtdatedisp.Columns.Add("Order_Date");
dtdatedisp.Columns.Add("Item_Quantity");
dtdatedisp.Columns.Add("TAXAMT");
dtdatedisp.Columns.Add("VALAMT");
dtdatedisp.Rows.Clear();
foreach (var g in drdatedisp)
{
DataRow newRow1 = dtdatedisp.NewRow();
newRow1[0] = g.Order_Date;
newRow1[1] = g.totalQnty;
newRow1[2] = String.Format("{0:0.00}", g.totalTax);
newRow1[3] = String.Format("{0:0.00}", g.totalAmt);
dtdatedisp.Rows.Add(newRow1);
}

Resources