I am developing a C# ASP.NET web application. I have data being pulled from two databases. One is the database that holds all of our actual data, the second is to be used so that users of the site can save "favorites" and easily find this data later. The databases have the following columns:
Table1:
itemid, itemdept, itemdescription
Table2:
userid, itemid, itemdept, itemdescription
If the item is present in table2 (the user has already added it), I want to mark the item as removable if it comes up again in a search, and addable if it has is not yet in their favorites.
I've got data from both pulled into datatables so I can compare them, but I feel that using a nested foreach loops will be too tedious as the query is set to return a max of 300 results. Also to do that, I have to put a bool value in one of the tables to mark that it was found, so this seems messy.
I have read up a little on Linq, but can't find anything exactly like this scenario. Could I use Linq to accomplish such a thing? Below is an (admittedly crude) image of the search results page that may help get a better grasp on this. In the real deal, the Add and Remove links will be imagebuttons.
Forgot to ever post the solution to this one, but I went with the HashSet setup, with one loop to compare. Thank you everyone for your comments.
if (User.Identity.IsAuthenticated)
{
DataColumn dc = new DataColumn("isMarked", System.Type.GetType("System.Int32"));
ds.Tables[0].Columns.Add(dc);
string[] strArray = ds.Tables[0].AsEnumerable().Select(s => s.Field<string>("itemid")).ToArray<string>();
HashSet<string> hset = new HashSet<string>(strArray);
foreach (DataRow dr in ds.Tables[0].Rows)
{
if (hset.Contains(dr["itemid"].ToString().Trim()))
dr[3] = 1;
else
dr[3] = 0;
}
}
Related
NOTE: I just realized Excel incremented the report numbers, ignore that they will be the same for each row per group.
I have a data table coming back from the database that looks something like this
Original Table Format
I am trying to get it to look like this in the Excel output. I have read articles on Outlining, but I am not sure that is what I am really after. I am trying to "group" the data by that case number field, but not repeat the data for each row, since it will be the same. The data that is different for each grouping is the payees and their amounts.
Formatted Output
Just looking for insight on using EPPlus for this or any advice from others who have come across something similar.
I believe this can be achieved with Linq. Not seeing any code it's a bit hard to produce a good example but the results should be achievable with the following idea:
First do a foreach where you fill in the excel cells with Case_Number, Docket, Agency_Name and Report_Number and then do a second foreach to go through the payments
var results = GetResults(); //However you retrieve your collection from DB
var groupedResults = results.GroupBy(a => new {
a.Case_Number,
a.Docket,
a.Agency_Name,
a.Report_Number}).Select(b => b.FirstOrDefault());
foreach(var group in groupedResults)
{
// Fill the cells
foreach(var payment in results.Where(a =>
a.Case_Number == group.Case_Number
&& a.Docket == group.Docket
&& a.Agency_Name == group.Agency_Name
&& a.Report_Number == group.Report_Number)
{
// Fill the payment detail cells and increment the row counter
}
}
Admittedly the second foreach is pretty rough, but I haven't found a better way. Hopefully someone is able to provide more elegant solution
I have two datatables with identical schemas, business partners and addresses. I'm trying to combine them in a specific format in order to import into another system.
Basically, I want the output to be as follows:
Business Partner
All associated addresses
Next business partner
All associated addresses
Here is the latest code I'm trying:
var finalDt = BpDt.Clone();
foreach(DataRow BpRow in BpDt.Rows)
{
finalDt.ImportRow(BpRow);
foreach(DataRow AddressRow in AddressDt.Rows)
{
if(Convert.ToString(BpRow["id"]).Equals(Convert.ToString(BpRow["id"])))
finalDt.ImportRow(AddressRow);
}
}
It seems to get caught in a infinite loop but I don't understand why. Is there a better way to approach this?
Your approach to this is terrible. But if you insist on going down this road, this should work:
var finalDt = BpDt.Clone();
foreach(DataRow BpRow in BpDt.Rows)
{
finalDt.ImportRow(BpRow);
foreach(DataRow AddressRow in AddressDt.Rows)
{
if(Convert.ToString(BpRow["id"]).Equals(Convert.ToString(AddressRow["id"])))
finalDt.ImportRow(AddressRow);
}
}
it seems like...
if(Convert.ToString(BpRow["id"]).Equals(Convert.ToString(BpRow["id"])))
will always be true. So you would just be inserting every AddressRow for each BpRow. Depending on your dataset size, this could be taking a really long time. Should the id comparison be this?
if(Convert.ToString(AddressRow["id"]).Equals(Convert.ToString(BpRow["id"])))
Conceptually this would be similar to a join on the id field.
A better approach might be to use LINQ. If you use the AsEnumerable() extension for DataTable you could query AddressDt using LINQ...
LINQ query on a DataTable
I have a piece of code that goes through all the linked tables and tables in an access database and for every table(all linked in this case) that matches a certain criteria it should add a new table and delete the old. The new is on a sql server database and the old the oracle, however this is irrelevant. The code is:
var dbe = new DBEngine();
Database db = dbe.OpenDatabase(#"C:\Users\x339\Documents\Test.accdb");
foreach (TableDef tbd in db.TableDefs)
{
if (tbd.Name.Contains("CLOASEUCDBA_T_"))
{
useddatabases[i] = tbd.Name;
string tablename = CLOASTableDictionary[tbd.Name];
string tablesourcename = CLOASTableDictionary[tbd.Name].Substring(6);
var newtable = db.CreateTableDef(tablename.Trim());
newtable.Connect = "ODBC;DSN=sql server copycloas;Trusted_Connection=Yes;APP=Microsoft Office 2010;DATABASE=ILFSView;";
newtable.SourceTableName = tablesourcename;
db.TableDefs.Append(newtable);
db.TableDefs.Delete(tbd.Name);
i++;
}
}
foreach (TableDef tbd in db.TableDefs)
{
Console.WriteLine("After loop "+tbd.Name);
}
There are 3 linked tables in this database 'CLOASEUCDBA_T_AGENT', 'CLOASEUCDBA_T_CLIENT' and 'CLOASEUCDBA_T_BASIC_POLICY'. The issue with the code is that it updates the first two tables perfectly but for some unknown reason, it never finds the third. Then in the second loop, it prints it out... it seems to just skip over 'CLOASEUCDBA_T_BASIC_POLICY'. I really dont know why. The weird thing is then that if run the code again, it will change 'CLOASEUCDBA_T_BASIC_POLICY'. Any help would be greatly appreciated.
Modifying a collection while you are iterating over it can sometimes mess things up. Try using a slightly different approach:
Iterate over the TableDefs collection and build a List (or perhaps a Dictionary) of the items you need to change. Then,
Iterate over the List and update the items in the TableDefs collection.
Basically, I need to get the email column from each table in the DataSet (there could be 0 tables in there, or there could be 100) and slap them together into a big List for processing later.
I was about to write the 2x nested loop to do it, but is there an easier way to type this?
My first attempt at loops didn't work so well, since DataTables don't define GetEnumerator :
foreach(DataTable item in emailTables.Tables)
{
foreach(var email in item)
{
// This doesn't work
}
}
Like L.B. said, it's opinion based, but LINQ would be my first choice. A bit less readability but less typing overall and definitely less nesting.
var listEmail = (from DataTable table in emailTables.Tables
from DataRow row in table.Rows
select row["yourColumnNameHere"].ToString()).ToList();
If any of the tables do not (or may not) have an email column, then you will have to do some more validation.
I chose to use a separate answer to extend the question posed in a comment after my initial answer was accepted.
The question was:
I'm considering making this into an extension method, would it be possible to extend this code to obtain multiple items, like Email, Name and Address? Maybe into something other than List<>?
The answer:
Create Anonymous types in a the select statement and assign different values from the columns to those types:
var selectedItems = from DataTable table in emailTables.Tables
from DataRow row in table.Rows
select
new
{
EMail = row["email"].ToString(),
Address = row["address"].ToString(),
Name = row["name"].ToString()
};
Then loop through the results in selectedItems and do whatever you would like to the fields. Not sure what type you want to store the results in, but this should give you a pretty good idea.
foreach (var item in selectedItems)
{
//Do whatever you want by accessing the fields EMail, Address, and Name using dot notation like
var myVar = item.EMail;
var myVar2 = item.Address;
//Etc... Not sure what the end result you need is going to be, but you should have a good starting point now.
}
OR you could just return the selectedItems collection. It's type is IEnumerable<T>.
I have data with the same schema in a pipe delimited text file and in a database table, including the primary key column.
I have to check if each row in the file is present in the table, if not generate an INSERT statement for that row.
The table has 30 columns, but here I've simplified for this example:
ID Name Address1 Address2 City State Zip
ID is the running identity column; so if a particular ID value from the file is found in the table, there should be no insert statement generated for that.
Here's my attempt, which doesn't feel correct:
foreach (var item in RecipientsInFile)
{
if (!RecipientsInDB.Any(u => u.ID == item.ID ))
{
Console.WriteLine(GetInsertSql(item));
}
}
Console.ReadLine();
EDIT: Sorry, I missed the asking the actual question; how to do this?
Thank you very much for all the help.
EDIT: The table has a million plus rows, while the file has 50K rows. This a one time thing, not a permanent project.
I would add all the RecipientsInDB Ids in a HashSet and then test if the set contains the item Id.
var recipientsInDBIds = new Hashset(RecipientsInDB.Select(u => u.ID));
foreach (var item in RecipientsInFile)
{
if (!recipientsInDBIds.Contains(item.ID ))
{
Console.WriteLine(GetInsertSql(item));
}
}
Console.ReadLine();
Try comparing the ID lists using .Except()
List<int> dbIDs = Recipients.Select(x=>x.ID).ToList();
List<int> fileIDs = RecipientsFile.Select(x=>x.ID).ToList();
List<int> toBeInserted = fileIDs.Except(dbIDs).ToList();
toBeInserted.ForEach(x=>GetInsertSqlStatementForID(x));
For the pedantic and trollish among us in the comments, please remember the above code (like any source code you find on the interwebs) shouldn't be copy/pasted into your production code. Try this refactoring:
foreach (var item in RecipientsFile.Select(x=>x.ID)
.Except(DatabaseRecipients.Select(x=>x.ID)))
{
GetInsertSqlStatementForID(item);
}
Lots of ways of accomplishing this. Yours is one way.
Another would be to always generate SQL, but generate it in the following manner:
if not exists (select 1 from Recipients where ID == 1234)
insert Recipients (...) values (...)
if not exists (select 1 from Recipients where ID == 1235)
insert Recipients (...) values (...)
Another would be to retrieve the entire contents of the database into memory beforehand, loading the database IDs into a HashSet, then only checking that HashSet to see if it exists - would take a little longer to get started, but would be faster for each record.
Any of these three techniques would work - it all depends on how big your database table is, and how big your file is. If they're both relatively small (maybe 10,000 records or so), then any of these should work fine.
EDIT
And there's always option D: Insert all records from the file into a temporary table (could be a real table or a SQL temp table, doesn't really matter) in the database, then use SQL to join the two tables together and retrieve the differences (using not exists or in or whatever technique you want), and insert the missing records that way.