Selecting distinct records from DataTable - c#

I've checked this question here as well as a number of other links all proposing similar solutions, but when I go distinctTable = dt.DefaultView.ToTable(true, "FILENAME"); I get an error message saying:
A field or property with the name 'LOCATION' was not found on the
selected data source.
Now, I have four columns altogether: Location, Folder, Filename, and Status. The values for Folder and Filename I select from the database, but the values for location and status are determined thru C# code. I have no idea why it's saying that the Location column isn't found because it works fine when I omit the above line.
I don't think I can use SQL because some of the records are being generated by user input (i.e. they aren't coming from the database).
I've also tried
view = new DataView(dt);
distinctTable = view.ToTable(true, "LOCATION", "FOLDER", "FILENAME", "STATUS");
or just distinctTable = view.ToTable(true, "FILENAME"); for the latter statement but this doesn't seem to do anything - it doesn't throw an exception but it doesn't eliminate duplicate records either.
What am I doing wrong?

It reads as if there are two issues:
You get an error when you databind to (I assume) your derived table;
When you try something else, you don't get an error, but you don't get the desired distinct records, either.
The code you post at the top:
distinctTable = dt.DefaultView.ToTable(true, "FILENAME");
... is going to give you a data table with exactly one column: FILENAME. So when you bind to it, you'll get an error if what you bind to is looking for a LOCATION column as well.
The code you post at the bottom:
view = new DataView(dt);
distinctTable = view.ToTable(true, "LOCATION", "FOLDER", "FILENAME", "STATUS");
... doesn't throw an error because it has LOCATION (and other columns) as part of the table, so the control you bind to is able to find all the columns.
But you say it doesn't remove duplicates. I'm wondering, when you say that you want to remove duplicates, but show code where you're only specifying one column for output, whether what you want is not to filter out exact duplicates of the entire record, but filter out records that have the same FILENAME value but different values of the other columns.
You can't (as far as I know) do that with DataView.ToTable. But you can do it with LINQ:
DataTable distinctTable = dt.AsEnumerable()
.GroupBy(r=> r.Field<string>("FILENAME"))
.Select(g=>g.First())
.CopyToDataTable();

How about using LINQ?
var items = yourdatatable.AsEnumerable().Distinct();

Related

Cannot remove this column, because it is part of an expression

I have a datatable and I am masking one column by adding new column name as MASKEDSSN.
It has been masked. But i need to Remove Old Column name SSN. While Removing it throws an error as
Cannot remove this column, because it is part of an expression:
MaskedSSN = 'XXX-XX-'+SUBSTRING(CONVERT(SSN, System.String),6,4).
Code:
DataTable employeeTable = new DataTable();
employeeTable.Rows.Add("123455789");
employeeTable.Rows.Add("123447789");
employeeTable.Rows.Add("823456719");
employeeTable.Columns.Add("SSN");
int index = employeeTable.Columns["SSN"].Ordinal;
employeeTable.Columns.Add("MaskedSSN", typeof(string));
employeeTable.Columns["MaskedSSN"].Expression = "'XXX-XX-
'+SUBSTRING(CONVERT(SSN, System.String),6,4)";
DataTable newsss = new DataTable();
newsss = employeeTable.Copy();
newsss.AcceptChanges();
newsss.Columns.RemoveAt(index);
newsss.Columns.Remove("SSN");
You are using Expression syntax to build MaskedSSN
employeeTable.Columns["MaskedSSN"].Expression = "'XXX-XX-
'+SUBSTRING(CONVERT(SSN, System.String),6,4)";
If you make masking manually with AddRow, you can delete old SSN Column.
Edit
Func<string, string> MaskingFnc = (string ssnParam) => string.Format("XXX-XX-{0}", ssnParam.Substring(4,6));
DataTable employeeTable = new DataTable();
employeeTable.Columns.Add("SSN")
employeeTable.Columns.Add("SSNMasked")
employeeTable.Rows.Add("123455789", MaskingFnc("123455789"));
employeeTable.Rows.Add("123447789", MaskingFnc("123447789"));
employeeTable.Rows.Add("823456719", MaskingFnc("823456719"));
Expressions, or computed columns, relies on the source column to have the data in, because it is referencing the data, it doesn't COPY the data and then mask it.
So if your original column data changes, the new column will automatically have the same value.
When you understand this dependency, you will realize that you can't just remove the source column, because if you do, you will have a null reference exception happening. Think of it as excel spreadsheet. Column A has a value, Column B uses Column A's value to determine a percentage (or show it in a currency format). If you delete Column A, Columb B will immediately get an error (#REF! or something)
Your question on CodeProject has more detail, which would be appreciated on StackOverflow too: https://www.codeproject.com/Questions/1272445/Cannot-remove-this-column-because-it-is-part-of-an
In there, you show you try to copy the table, and then remove the column from it, while still having the expression, which results in an error.
Hopefully my explanation highlights the dependency between a column and a computed column/expression.
I would suggest formatting the data in the front-end (that's why we have front-end), or, if it's part of an API, format it in the SQL output.
Any input can also be masked so that data is inserted correctly BEFORE having to read it out anywhere.
You can mask manually using string.Format (note, below might not compile, I leave it to you to figure out)
string result = string.Format("XXX-XX-{0}", SSNColumnValue.Substring(4,6));
Hope this helps guide you in the right direction

Cannot fill temporary table from gridview

During a gridview => database update function, I use a column-by-column conversion to string in order to pass data back to my database as a whole command string. I'm aware that this sounds convoluted, so here is an example:
Classes in use:
InvoiceHandler.cs
Default.aspx.cs
Since this is a rather large snippet, here is a pastebin:
Default.aspx.cs: http://pastebin.com/Y3fJZ36Z
InvoiceHandler.cs: http://pastebin.com/ZsdAnDxr
At the first point of conversion (invoiceTableEdited.Columns["Column1"].ColumnName = "#K_INV";) I get a NullReferenceException error, assumedly because the method call in Default.aspx.cs
handler.invoiceTableEdited = ViewState["invoiceTable"] as DataTable;
handler.invoiceTableEdited.Rows[row.RowIndex]["K_INVOICE"] = sK_INVOICE;
appears to be having trouble.
What must I do to resolve this?
I'll be honest, it's not very clear how you're trying to create the column collection for the table 'InvoiceTableEdited'. What it looks like you're doing in OnUpdate is you are assigning a string variable to a row in the table with a given index and a column that doesn't currently exist. Your basically saying put this string into a cell with a row number of 'x' and a column name of "column1". At this point "column1" doesn't exist.
I'd create the columns you need first in InvoiceHandler.cs like this (assumes they're string):
invoiceTableEdited.Columns.Add("MyColumn1", typeof(string));
invoiceTableEdited.Columns.Add("MyColumn2", typeof(string));
Obviously if you have loads of columns and you don't care about their names then just create a loop and add them that way. This will give them the naming convention "ColumnN", where 'N' is the number. You can then assign a name to them by referencing there name ("Column1" for example) or using their index.

Convert Sorted DataView to Typed DataTable

i have a strong typed DataTable named Account wich i sorted on Account.FullName:
DataView dvAccount = new DataView(dtAccount)
dvAccount.Sort = "FullName desc";
The fullname is a generated field from my DataSet after my query, based on first name, middle, last etc. This means that sorting by SQL query is not an option unfortunately.
First i tried to get the table like this:
dtAccount = dvAccount.Table()
But this gave me the original Table the Dataview was based on. So after reading online i found out that i should have used the DataView.ToTable() function instead of the DataView.Table() function:
dtAccount = dvAccount.ToTable() as dsAccount.AccountDataTable; // returns null
dtAccount = ((dsAccount.AccountDataTable) dvAccount.ToTable()); // gives Convertion to Typed Datatable Error
Now i get the problem that my Account Table is Strong typed. so searching online tells me that i could go with the DataTable.Merge() Function or DataTable.ImportRow() for each row but these are told to be a very heavy procedures because every row gets checked on the Type. what's the best practice solution to this situation?
I just had the same issue. I used this kind of solution.
dtAccount = New dsAccount.AccountDataTable;
dtAccount.Merge(dvAccount.ToTable());
This works fine for me.
Tell me if you have a better one.

DataRow[] itemsarray

I can't seem to be able to be access elements that my DataRow has pulled from my DataTable, I haven't had that much practice with c# either
This is my code:
DataRow[] results = dt.Select("[Acc No] = '"+ search +"'");
I have tried casting teh objects from datarow to a string but that was not working.
Search is just a string from a textbox.
When debugging i can see the items array with all the data in it so i know the select is working, can anyone help?
You need to provide more code that that... Such as how you're trying to access the contents of a DataRow. To get a value out of the row, I believe the syntax would be something like results[rowNumber][columnNumber/name]
I.e. results[0][0] to get the first column value out of the first row, or results[0]["Id"] to get the "Id" column from the first row.
Of course you should check results.Count() before attempting to access the DataRow array.

A DataTable named 'Table' already belongs to this DataSet

when i am adding second table(dtResult) to data set that time it's giving error
A DataTable named 'Table' already belongs to this DataSet.
DataTable dtSession = new DataTable();
DataTable dtResult= new DataTable();
dtResult.TableName = "A";
dtSession.TableName = "B";
dtSession = objOpt.GetSearchDetails().Copy();
ds.Tables.Add(dtSession);
dtResult = objOpt.Search_Synchronous().Copy();
ds.Tables.Add(dtResult);
Thanks in advance
You need to name the tables after getting the copy from your method and before adding it to the DataSet.
DataTable dtResult= new DataTable();
dtSession = objOpt.GetSearchDetails().Copy();
dtSession.TableName = "B";
ds.Tables.Add(dtSession);
dtResult = objOpt.Search_Synchronous().Copy();
dtResult.TableName = "A";
ds.Tables.Add(dtResult);
Since you are getting the copy from your methods objOpt.GetSearchDetails().Copy() and objOpt.Search_Synchronous().Copy(), they are overwriting the names assigned to the table previously, and both of these are returning the table with name Table, that is why you are getting this error
I was getting this exception today, but it had nothing to do with adding a DataTable.
I have an ASP.Net Core WebApi, and in the Post and Put endpoints, I was attempting to save/update the new record and also search in the database (in the same table) for an existing record with the same details, and if such a record exists, to update it.
I was getting that exception when it tried to save these changes.
The solution was to split it up, save the new/updated record first...
// Avoid an "A DataTable named 'Users' already belongs to this DataSet." exception
await _context.SaveChangesAsync();
... and then check for an existing record, update it if necessary, and then doing a separate save...
await _context.SaveChangesAsync();
Yup, it's weird, but saving twice fixed this issue for me.
(I appreciate that this doesn't answer this exact StackOverflow question, but for anyone stumbling onto this page when they hit this strangely-worded exception, this'll be a lifesaver !)
I received this error when running command.ExecuteDataSet on a stored procedure where I declared a table variable then queried that table multiple times to return several datatables in the dataset. Because each datatable was queried from the same table, they all had the same name '#t'. I was able to resolve it by creating multiple table variables with different names in the stored procedure.

Categories