Hello I have two Datasets with the same schemas and i need to get changes between two of them.
Datasets can be created using code below:
DataSet First = new DataSet("DSStore");
DataTable Footer = new DataTable("Footer");
DataColumn Column = new DataColumn("Value", Type.GetType("System.Int32"), "");
DataColumn[] PK = new DataColumn[1];
PK[0] = Column;
DataSet changes;
First.Tables.Add(Footer);
Footer.Columns.Add(Column);
Footer.PrimaryKey = PK;
//Clone to create second one
changes = First.Clone();
now just fill both with data:
for (int i = 0; i < 10; i++)
{
var row2 = changes.Tables["Footer"].NewRow();
row2["Value"] = i;
changes.Tables["Footer"].Rows.Add(row2);
}
var firstRow = First.Tables["Footer"].NewRow();
firstRow["Value"] = 8;
First.Tables["Footer"].Rows.Add(firstRow);
First.AcceptChanges();
changes.AcceptChanges();
Now when we have all data prepared we can get to what I tried:
I tried merging both of them:
First.Merge(changes);
if (First.HasChanges())
Console.WriteLine("has changes");
else
Console.WriteLine("Doesnt");
but unfortunately merge do not change row status so after rows being accepted hasChanges returns false and getchanges is null.
I tried another way:
IEnumerable<DataRow> added = changes.Tables["Footer"].AsEnumerable().Except(First.Tables["Footer"].AsEnumerable(),DataRowComparer.Default);
Console.WriteLine("Added:");
foreach (var row in added)
{
Console.WriteLine(row["Value"]);
}
Now i received some results but it is printing all 9 lines correct. So I tried to insert changes to the first dataset:
foreach(var row in added)
{
changes2.Tables["Footer"].Rows.Add(row);
}
if (changes2.HasChanges())
Console.WriteLine("has changes");
else
Console.WriteLine("Doesnt");
But after trying to add rows I am receving ArgumentException
I needed to change one line:
foreach(var row in added)
{
changes2.Tables["Footer"].Rows.Add(row.ItemArray);
}
now its ok. Adding lines creates changes
Related
BatchUpdateSpreadsheetRequest requestBody = new BatchUpdateSpreadsheetRequest();
requestBody.Requests = new List<Request>();
Request r = new Request();
requestBody.Requests.Add(r);
r.UpdateCells = new UpdateCellsRequest();
var gc = new GridCoordinate();
gc.ColumnIndex = 0;
gc.RowIndex = 0;
gc.SheetId = 0;
r.UpdateCells.Start = gc;
r.UpdateCells.Fields = "*";
r.UpdateCells.Rows = new List<RowData>();
//TODO:: loop through record set to update cell data (cd) with values to insert
SqlDataReader reader = default(SqlDataReader);
reader = o_cSQL.RunSPReturnDataReader("Shippments", 40997, sNow, sNow);
while (reader.Read())
{
var rd = new RowData();
r.UpdateCells.Rows.Add(rd);
rd.Values = new List<CellData>();
var cd = new CellData();
cd.UserEnteredValue = new ExtendedValue();
// the next line is only updating the first cell (first row/first column)
//how to you get multiple CellData created for a single row ?
cd.UserEnteredValue.StringValue = reader["new_attn"].ToString();
rd.Values.Add(cd);
}
SpreadsheetsResource.BatchUpdateRequest batchRequest = service.Spreadsheets.BatchUpdate(requestBody, spreadsheetId);
batchRequest.Execute();
Can someone please help with how you get multiple cells updated per row ?
I know I'm only showing one value from my 'reader' from the database, that's where I got stuck in that I don't know how to specifically pinpoint each cell of my spreadsheet. I'm pulling an unknown recordset size from a database and need to enter it on a google spreadsheet. I know how many columns.
Since I know my columns, I just kept making new CellData(); items for each RowData item in my reader.Read() while looping. Worked first time!
I'm using ClosedXML elsewhere in my script where I'm iterating through every row like this and it works.
var workbook = new XLWorkbook(ObjectRepPath);
var rows = workbook.Worksheet(1).RangeUsed().RowsUsed().Skip(1);
foreach (var row in rows)
{
objPage = row.Cell(1).GetString();
objElement = row.Cell(2).GetString();
if (objPage == page && objElement == element)
{
locType = row.Cell(3).GetString();
locParm = row.Cell(4).GetString();
}
}
After that I need to pull the data from the cells in a randomly selected row. Here's what I've got so far, which is not working...
var workbook = new XLWorkbook(extFile);
var ws = workbook.Worksheets.Add("Cell Values");
var rnd = new Random();
int rowNum = rnd.Next(2, workbook.Worksheet(1).RangeUsed().RowsUsed().Count());
var dataRow = ws.Row(rowNum);
string dangit = dataRow.Cell(1).GetString();
System.Diagnostics.Debug.WriteLine("Why is this dang thing not working... " + dangit);
Output: Why is this damn thing not working...
It just comes back empty. No error. Does anyone see something I don't?
Alright, I found the solution.
I changed the line ...
var ws = workbook.Worksheets.Add("Cell Values");
to ....
var ws = workbook.Worksheet(1);
and now this works ....
Storage.StreetAddress = ws.Cell(xlRow, 1).GetString();
I'm new to C#, I never worked with a DataTable before.
I want a DataGridView with specific names.
DataTable table = new DataTable();
List<string> bla = new List<string>();
XDocument config = XDocument.Load(configFile);
Dictionary<string, string> dict = config.Descendants("Columns").FirstOrDefault().Elements()
.GroupBy(x => (string)x.Attribute("XPath"), y => (string)y.Attribute("Name"))
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
//I dont know if I need this:
foreach (string key in dict.Keys)
{
table.Columns.Add(key, typeof(string));
}
foreach (XElement position in positions.Where(e => e.HasAttributes))
{
foreach (XAttribute attribute in position.Attributes().Where(a => dict.ContainsKey($"#{a.Name.LocalName}")))
{
string name = attribute.Name.LocalName;
string value = (string)attribute;
string xName = dict["#" + name];
bla.Add(xName);
}
The columns should have the name from xName.
How can I do this?
I've tried this:
foreach (var item in bla)
{
DataRow row = table.NewRow();
row.SetField<string>(item); //this didn't work.
//foreach (string key in dict.Keys)
//{
// row.SetField<string>(key, item[key]);
//}
}
Just want the names from xName as my heading for the output.
Example für xName: Position, Status, Order, Number, ...
As my heading.
And under that the values.
if i understand you correctly, you've got your list of column names ok, but dont know how to create a datatable with the correct column names.
Below is an example of how to add a column and row to a datatable with a specific column header name.
As discussed in the comments, I've demonstrated a process to get the data you need into a structure that allows you to populate your table.
//Class to hold data
public class MyRecordContent
{
public MyRecordContent()
{
//initialise list
RecordsColumns = new List<string>();
}
//Holds a list of strings for each column of the record.
//It starts at position 0 to however many columns you have
public List<string> RecordsColumns { get; set; }
}
//This creates an empty table with the columns
var myTable = new DataTable("Table1");
foreach (var item in bla)
{
if (!myTable.Columns.Contains(item))
{
myTable.Columns.Add(new DataColumn(item, typeof(string)));
}
}
//Here you build up a list of all records and their field content from your xml.
foreach (var xmlNode in yourXMLRecordCollection)
{
var thisRecord = new MyRecordContent();
foreach (var xmlCol in xmlNode.Elements)//Each column value
{
thisRecord.RecordsColumns.Add(xmlCol.GetValue());
}
myListOfRecords.Add(thisRecord);
}
foreach (MyRecordContent record in myListOfRecords)
{
var row = myTable.NewRow();
//Here we set each row column values in the datatable.
//Map each rows column value to be the value in the list at same position.
for (var colPosition = 0; colPosition <= myTable.Columns.Count - 1;) //Number of columns added.
{
row[colPosition] = record.RecordsColumns[colPosition];
}
myTable.Rows.Add(row);
}
In the above, itterate through your list of column names and add each column to the table. You may want to add a switch statement to the loop to change the datatype of the column based upon name if required. Then create of new row off that table and set each fields value accordingly.
Finally, add the new row to the datatable.
Hope that helps.
Then
I am trying to remove rows that are not needed from a DataTable. Basically, there may be several rows where the itemID is identical. I want to find the rows where the column "failEmail" = "fail", and using the itemID of those rows, remove all rows from the emails DataTable that have the same itemID.
Here is what I have tried:
System.Diagnostics.Debug.Print(emails.Rows.Count.ToString() + " emails!");
// create a list of the email IDs for records that will be deleted
List<DataRow> rows2Delete = new List<DataRow>();
foreach (DataRow dr in emails.Rows)
{
if (dr["failEmail"].ToString().ToLower() == "fail")
{
rows2Delete.Add(dr);
}
}
foreach (DataRow row in rows2Delete)
{
DataRow[] drRowsToCheck =emails.Select("itemID ='" + row["itemID"].ToString() +"'");
foreach (DataRow drCheck in drRowsToCheck)
{
emails.Rows.RemovedDrCheck);
emails.AcceptChanges();
}
}
Here is the error message I am getting on the second pass:
This row has been removed from a table and does not have any data.
BeginEdit() will allow creation of new data in this row
How can I do what I need to without throwing errors like that? Is there a better way like using a LiNQ query?
The problem is that when the same itemID has multiple entries with 'fail', you are trying to remove them multiple times.
// 1. Find the Unique itemIDs to remove
var idsToRemove = emails.Select("failEmail = 'fail'").Select (x => x["itemID"]).Distinct();
// 2. Find all the rows that match the itemIDs found
var rowsToRemove = emails.Select(string.Format("itemID in ({0})", string.Join(", ", idsToRemove)));
// 3. Remove the found rows.
foreach(var rowToRemove in rowsToRemove)
{
emails.Rows.Remove(rowToRemove);
}
emails.AcceptChanges();
this is what I ended up doing, based on an answer I got from MSDN c# Forums:
create an extension on DataTable to enable LINQ euering of the Datatable:
public static class DataTableExtensions
{
public static IEnumerable<DataRow> RowsAsEnumerable ( this DataTable source )
{
return (source != null) ? source.Rows.OfType<DataRow>() : Enumerable.Empty<DataRow>();
}
}
then modified my code as below:
//Get IDs to delete
var deleteIds = from r in emails.RowsAsEnumerable()
where String.Compare(r["failEmail"].ToString(), "fail", true) == 0
select r["itemID"];
//Get all rows to delete
var rows2Delete = (from r in emails.RowsAsEnumerable()
where deleteIds.Contains(r["itemID"])
select r).ToList();
//Now delete them
foreach (var row in rows2Delete)
emails.Rows.Remove(row);
emails.AcceptChanges();
and now it works, just wish I could do it the normal way successfully.
foreach (DataRow rowFail in emails.Select("failEmail = 'fail'"))
{
DataRow[] rowsItem = emails.Select(String.Format("itemID = '{0}'", rowFail["itemID"]));
for (int i = rowsItem.Length - 1; i >= 0; i--)
{
rowsItem[i].Delete();
}
}
emails.AcceptChanges();
DataTable.Select returns an array of all DataRow objects that match the filter criteria.
I need to change the DateTimeMode of some columns in an already populated dataset. (I don't want to change it before it gets populated as it would mean making changes in several methods throughtout the application.)
Here's the stmt I am using (for a single column):
copy.Tables[0].Columns["DateColName"].DateTimeMode = DataSetDateTime.Utc;
However, it throws an error that you can't change the DateTimeMode if the dataset contains data. So the solution I am thinking is creating a clone of the dataset, changing the DateTimeMode of required columns and then re-loading data back.
DataSet copy = dsdata.Clone();
copy.Tables[0].Columns["DateColName"].DateTimeMode = DataSetDateTime.Utc;
copy.Load(dsdata.CreateDataReader(), LoadOption.OverwriteChanges, "TableName");
Is there a better way of doing this??
try this, cheers
private void SetUtcDateTime()
{
var ds = new DataSet { Locale = CultureInfo.InvariantCulture };
foreach (DataTable source in DataSet.Tables)
{
bool containsDate = false;
var target = source.Clone();
foreach (DataColumn col in target.Columns)
{
if (col.DataType == System.Type.GetType("System.DateTime"))
{
col.DateTimeMode = DataSetDateTime.Utc;
containsDate = true;
}
}
if (containsDate)
{
foreach (DataRow row in source.Rows)
target.ImportRow(row);
ds.Tables.Add(target);
}
else
{
ds.Tables.Add(source.Copy());
}
}
DataSet.Tables.Clear();
DataSet = ds;
}
where 'DataSet' is a public property on your object.
I just ran into this issue also. The DateTimeMode cannot be changed once the dataset has been filled, so the only solution I could find is to re-create the column with the correct DateTimeMode.
This code might help, you don't have to clone the entire dataset, just remove the column, modify it and add it back to the table.
private static void SetDateTimeMode(DataTable table, DataColumn col, DataSetDateTime mode)
{
var rowValues = new object[table.Rows.Count];
for (int i = 0; i < rowValues.Length; i++)
{
// ignore deleted rows
if (table.Rows[i].RowState == DataRowState.Deleted) continue;
rowValues[i] = table.Rows[i][col];
}
// we must remove and re-add the row because DateTimeMode cannot be
// changed on a column that has data.
table.Columns.Remove(col);
col.DateTimeMode = mode;
table.Columns.Add(col);
// write back each row value
for (int i = 0; i < rowValues.Length; i++)
{
// ignore deleted rows
if (table.Rows[i].RowState == DataRowState.Deleted) continue;
var rowState = table.Rows[i].RowState;
table.Rows[i][col] = rowValues[i];
// preserve unchanged rowstate
if (rowState == DataRowState.Unchanged)
table.Rows[i].AcceptChanges();
}
}
You just have to be careful, when you copy the row values back to the column to preserve the RowState.