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
Related
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 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;
}
}
I am trying to create a friendly report summing enrollment for number of students by time of day. I initially started with loops for campusname, then time, then day and hibut it was extremely inefficient and slow. I decided to take another approach and select all the data I need in one select and organize it using c#.
Raw Data View
My problem is I am not sure whether to put this into arrays, or lists, or a dictionary or datatable to sum the enrollment and organize it as seen below(mockup, not calculated). Any guidance would be appreciated.
Friendly View
Well, if you only need to show the user some data (and not edit it) you may want to create a report.
Otherwise, if you only need sums, you could get all the data in an IEnumerable and call .Sum(). And as pointed out by colinsmith, you can use Linq in parallel.
But one thing is definite though... If you have a lot of data, you don't want to do many queries. You could either use a sum query in SQL (if the data is stored in a database) or do the sum from a collection you've fetched.
You don't want to fetch the data in a loop. Processing data in memory is way faster than querying multiple times the database and then process it.
Normally I would advise you to do this in the database, i.e. a select using group by etc, I'm having a bit of trouble figuring out how your first picture relates to the second with regards to the days so I can't offer an example.
You could of course do this in C# as well using LINQ to objects but I would first try and solve it in the DB, you are better of performance and bandwidth wise that way.
I am not quite sure what you are exactly after. But from my understanding, i would suggest you to create a class to represent your enrollment
public class Enrollment
{
public string CampusName { set;get;}
public DateTime DateEnrolled { set;get;}
}
And Get all enrollment details from the database to a collection of this class
List<Enrollment> enrollments=db.GetEnrollments();
Now you can do so many operations on this Collection to get your desired data
Ex : If you want to get all Enrollment happened on Fridays
var fridaysEnrollMent = enrollments.
Where(x => x.DateEnrolled.DayOfWeek == DayOfWeek.Friday).ToList();
If you want the Count of Enrollments happened in AA campus
var fridayCount = fridaysEnrollMent .Where(d => d.CampusName == "AA").Count();
something like
select campusname, ssrmeet_begin_time, count(ssrmeet_monday), count(ssrmeet_tue_day) ..
from the_table
group by campusname, ssrmeet_begin_time
order by campusname, ssrmeet_begin_time
/
should be close to what you want. The count only counts the values, not the NULL's. It is also thousands of times faster than first fetching all data to the client. Let the database do the analysis for you, it already has all the data.
BTW: instead of those pics, it is smarter to give some ddl and insert statements with data to work on. That would invite more people to help to answer the question.
How do you get a list of all the tables and use that list to enumerate the columns?
I've found posts that describe one or the other, but not both.
My net-result is I want to generate a static class which contains names of all the columns in each tables so I could, for example, do:
comboBoxFoo.DisplayMember = SomeNamespace.SomeTable.SomeDisplayColumnName;
comboBoxFoo.ValueMember = SomeNamespace.SomeTable.SomeIDColumnName;
comboBoxFoo.DataSource = dingo;
I'm currently using this method which while it works, it means I have to manually create my tables in a list.
I have a seperate command line project which generates the SomeNameSpace.SomeTable class manually and I add the generated class file in to the project.
Ideally, if I could loop through via a foreach of tables and do something like this:
foreach(var table in someTableNumerationMethodForAGivenContext())
{
var columnList = databaseContext.ColumnNames<someTable>();
foreach(var columnData in columnList)
{
DoJazz(columnData.Name);
}
}
Is there a better way to do this other than manually have to do the databaseContext.ColumnNames() ?
Edit 1:
We're using LinqToSQL. Moving to ADO.NET also an option on the table, but at the moment we have no pressing need to.
Edit 2:
I know L2S does databinding but what I'm after is getting a list of column names as strings from a table. L2S doesn't offer this or it's not apparent on my side.
I'd like to do something like: SomeTable.SomeColumn.ToString() or something. SubSonic has this.
Final:
Thanks everyone. all are very good answers and lead me to the answer. You guys rock!
Nazadus,
Is this what you are looking for?
LINQ to SQL Trick: Get all Table [and Column] Names:
http://blogs.msdn.com/jomo_fisher/archive/2007/07/30/linq-to-sql-trick-get-all-table-names.aspx
What you are describing is essentially an ORM
Linq to SQL is an ORM that will create prototype c# classes for you that contain the information you are describing. It has excellent support for the kind of data binding that you have illustrated.
I think you are looking for this.
DataContext.Mapping.GetTable(yourTable).RowType.DataMemebers()
I can think of two ways to do this.
A) Use SMO to get all the tables / columns in a database. You need to reference:
Microsoft.SqlServer.ConnectionInfo
Microsoft.SqlServer.Management.Sdk.Sfc
Microsoft.SqlServer.Smo
Then, you can do something like this:
ServerConnection connection = new ServerConnection(".");
Server server = new Server(connection);
Database db = server.Databases["Northwind"];
foreach (Table table in db.Tables)
{
foreach (Column column in table.Columns)
{
Console.WriteLine("Table: {0}, Column: {1}",table.Name,column.Name);
}
}
}
B) Use reflection to reflect over your assembly with the dataclasses generated by Linq to Sql. Anything with a Table attribute is a table, and anything with a Column attribute is a column. Let me know if you need a sample...
I have two datatables. One is a superset of another. Both have one column StageID common. I need to merge these two tables together so that the subset datatables data gets stored in the correct row of superset table.
This is what i did to join the data after retrieving the superset data in JoinTable and subset data in DTMaterialProperties table
foreach (DataColumn dcMaterialProp in dtMaterialProperties.Columns)
{
if (dcMaterialProp.ColumnName != "StageId" &&
JoinDataTable.Columns.Contains(dcMaterialProp.ColumnName))
{
JoinDataTable.Rows[rowCount][dcMaterialProp.ColumnName] =
dtMaterialProperties.Rows[0][dcMaterialProp.ColumnName];
}
}
But this was not efficient as it takes a lot of time in looping through.
Please Help me in finding a better way to do this.
If you can use LINQ, an easy way would be to use the Join extension method.
See Join Method-Based Query Syntax Examples (LINQ to DataSet)
If you don't have access to LINQ to DataSet
...you can check out this link from MSDN on how to implement a DataSet JOIN Helper Class.
HOW TO: Implement a DataSet JOIN helper class is Visual C# .NET
What you are really looking for seems to be a relationship. If you add both datatables to the same dataset, you can define the relationship. See Define a relationship between two tables in a DataSet in VB .NET
Then, all you have to do is parentRow.GetChildRows() to get the associated child rows.