Reading in list using SqlDataReader C# - c#

I have to fill in some lists in while loop as:
while (_myReader_1.Read())
{
_Row_Counter++;
int _authorID = _myReader_1.GetInt32(0);
Author _author = _eAthors.FirstOrDefault(_a => _a._AuthorID == _authorID);
if (_author == null)
{
_author = new Author
{
_AuthorID = _authorID,
_AuthorName = _myReader_1.GetString(1),
_Attributes = new List<AuthorAttributes>()
};
}
var _attribute = new AuthorAttributes()
{
_PaperID = new List<int>(),
_CoAuthorID = new List<int>(),
_VenueID = new List<int>()
};
_attribute._PaperID.Add(_myReader_1.GetInt32(2));
_attribute._CoAuthorID.Add(_myReader_1.GetInt32(3));
_attribute._VenueID.Add(_myReader_1.GetInt32(4));
_attribute._Year = _myReader_1.GetInt32(5);
_author._Attributes.Add(_attribute);
_eAthors.Add(_author);
}
_myReader_1.Close();
The data in SQL table looks like:
Author_ID | Author_Name | Paper_ID | CoAuthor_ID | Venue_ID | Year
------------------------------------------------------------------
677 | Nuno Vas | 812229 | 901706 | 64309 | 2005
677 | Nuno Vas | 812486 | 901706 | 65182 | 2005
677 | Nuno Vas | 818273 | 901706 | 185787 | 2005
677 | Nuno Vas | 975105 | 901706 | 113930 | 2007
677 | Nuno Vas | 975105 | 1695352 | 113930 | 2007
... | ... | ... | ... | ... | ...
The issue is each time loop iterates, new lists _PaperID, _CoAuthorID and _VenueID are created, which is not desired. As we have a check if(author == null), then to create a new author, similarly I want to check if a list for _PaperID exists for an author e.g. for Author_ID = 677, then to Add in same list until Author_ID get changed.
Also until the Author_ID = 677, the list _eAuthors should have Count = 1
I'm attaching some images to refine the problem.
Image 1: Showing eAuthors Count = 3, Attributes Count = 3 for AuthorID = 677, while 3 of iterations passed whereas eAuthors Count should = 1.
Image 2: Showing Individual Attribute lists for each row, as in 3rd iteration the Attribute e.g. CoAuthorID, the Count = 1, whereas it should be = 3 while in 3rd iteration and same for rest of the Attributes

Following the data structure shown and seeing what depicted in images, it seems that all attributes (Paper, CoAuthor, Venue) are of type lists, so there is no need to declare attributes as List<AuthorAttributes>. Follow this to what you want to achieve:
while (_myReader_1.Read())
{
_Row_Counter++;
int _authorID = _myReader_1.GetInt32(0);
Author _author = _eAthors.FirstOrDefault(_a => _a._AuthorID == _authorID);
if (_author == null)
{
_author = new Author
{
_AuthorID = _authorID,
_AuthorName = _myReader_1.GetString(1),
_Attributes = new AuthorAttributes()
};
}
// Check if list _PaperID doesn't exist
if (_author._Attributes._PaperID == null)
{
// Create new _PaperID
_author._Attributes._PaperID = new List<int>();
// Add Paper_ID to _PaperID
_author._Attributes._PaperID.Add(_myReader_1.GetInt32(2));
}
else // Add Paper_ID to existing _PaperID list
_author._Attributes._PaperID.Add(_myReader_1.GetInt32(2));
// Check if list _CoAuthorID doesn't exist
if (_author._Attributes._CoAuthorID == null)
{
// Create new _CoAuthorID
_author._Attributes._CoAuthorID = new List<int>();
// Add CoAuthor_ID to _CoAuthorID
_author._Attributes._CoAuthorID.Add(_myReader_1.GetInt32(3));
}
else // Add CoAuthor_ID to existing _CoAuthorID list
_author._Attributes._CoAuthorID.Add(_myReader_1.GetInt32(3));
// Check if list _CoAuthorID doesn't exist
if (_author._Attributes._VenueID == null)
{
// Create new _VenueID
_author._Attributes._VenueID = new List<int>();
// Add Venue_ID to _VenueID
_author._Attributes._VenueID.Add(_myReader_1.GetInt32(4));
}
else // Add Venue_ID to existing _VenueID list
_author._Attributes._VenueID.Add(_myReader_1.GetInt32(4));
// Add Year to _Year
_author._Attributes._Year =_myReader_1.GetInt32(5);
if (!_eAthors.Contains(_author))
_eAthors.Add(_author);
}
_myReader_1.Close();

Assuming your data structure looks like this:
Author
AuthorAttributes
Papers (list)
PaperID
CoAuthors (list)
CoAuthorID
Venues (list)
VenueID
Year
You could try this:
while (_myReader_1.Read())
{
_Row_Counter++;
int _authorID = _myReader_1.GetInt32(0);
string _authorName = _myReader_1.GetString(1);
int _paperID = _myReader_1.GetInt32(2);
int _coAuthorID = _myReader_1.GetInt32(3);
int _venueID = _myReader_1.GetInt32(4);
int _year = _myReader_1.GetInt32(5);
Author _author = _eAthors.FirstOrDefault(_a => _a._AuthorID == _authorID);
if (_author == null)
{
_author = new Author
{
_AuthorID = _authorID,
_AuthorName = _authorName,
_AuthorAttributes = new AuthorAttributes
{
_Papers = new List<int>(),
_Venues = new List<int>(),
_Year = _year,
_CoAuthors = new List<int>()
}
};
_eAthors.Add(_author); // only add if author not found
}
if ( !_author._AuthorAttributes._Papers.Contains( _paperID ) )
_author._AuthorAttributes._Papers.Add( _paperID );
if ( !_author._AuthorAttributes._CoAuthors.Contains( _coAuthorID ) )
_author._AuthorAttributes._CoAuthors.Add( _coAuthorID );
if ( !_author._AuthorAttributes._Venues.Contains( _venueID ) )
_author._AuthorAttributes._Venues.Add( _venueID );
}
_myReader_1.Close();

Add the newly created author right after initialization in the author == null check. Then check if author.PaperID == null and if it is, add the AuthorAttributes. Like this:
while (_myReader_1.Read())
{
_Row_Counter++;
int _authorID = _myReader_1.GetInt32(0);
Author _author = _eAthors.FirstOrDefault(_a => _a._AuthorID == _authorID);
if (_author == null)
{
_author = new Author
{
_AuthorID = _authorID,
_AuthorName = _myReader_1.GetString(1),
_Attributes = new List<AuthorAttributes>()
};
_eAthors.Add(_author); // ********** Add the new author
}
// Watch out!!! author.Attributes may be null for existing authors!!!
if (author.Attributes.PaperID == null || author.PaperID.Count == 0) // Check for PaperID existence
{
var _attribute = new AuthorAttributes()
{
_PaperID = new List<int>(),
_CoAuthorID = new List<int>(),
_VenueID = new List<int>()
};
_attribute._PaperID.Add(_myReader_1.GetInt32(2));
_attribute._CoAuthorID.Add(_myReader_1.GetInt32(3));
_attribute._VenueID.Add(_myReader_1.GetInt32(4));
_attribute._Year = _myReader_1.GetInt32(5);
_author._Attributes.Add(_attribute);
}
}
_myReader_1.Close();
Of course, if necessary you can process each attribute separately, by adding an if block for each one.

Related

C# Create group boxes dynamically depending on data received in Windows Forms

I am currently working on windows forms application, and I want to dynamically create form, which creates GroupBox depending on categories and products.
I have two tables like:
+-------------------------------+
| Category |
+-------------------------------+
| CategoryId (UNIQUEIDENTIFIER) |
| Name (VARCHAR(255)) |
| CreatedBy(VARCHAR(255)) |
| CreationDate(DATETIME) |
+-------------------------------+
+-----------------------------+
| Product |
+-----------------------------+
| ProductId(UNIQUEIDENTIFIER) |
| CategoryId (FK) |
| Name(VARCHAR(255)) |
| CreatedBy(VARCHAR(255)) |
| CreationDate(DATETIME) |
| IsDeleted(BOOL) |
+-----------------------------+
For example if I have category Fruit and its products are Banana and Apple, but I have another category called Vegetables and its products are Avocado and Tomato like:
+--------------------------------------+---------------------+-------------------------+------------------------+
| CategoryId (UNIQUEIDENTIFIER) | Name (VARCHAR(255)) | CreatedBy(VARCHAR(255)) | CreationDate(DATETIME) |
+--------------------------------------+---------------------+-------------------------+------------------------+
| CEF50872-B8E7-4EA0-A3D7-00048FFC82DA | Fruit | Test | 10/11/2018 |
| 646B5A64-EA28-4471-8964-0017EB511797 | Vegetables | Test | 10/12/2018 |
+--------------------------------------+---------------------+-------------------------+------------------------+
+--------------------------------------+--------------------------------------+---------+-------------------------+------------------------+
| ProductId(UNIQUEIDENTIFIER) | CategoryId (FK) | Name | CreatedBy(VARCHAR(255)) | CreationDate(DATETIME) |
+--------------------------------------+--------------------------------------+---------+-------------------------+------------------------+
| D81EFF8C-9B8D-48C9-9CC7-0015B2787D4A | CEF50872-B8E7-4EA0-A3D7-00048FFC82DA | Banana | Test | 10/11/2018 |
| 7683554C-5E46-40FE-9285-001E8CD67740 | CEF50872-B8E7-4EA0-A3D7-00048FFC82DA | Apple | Test | 10/11/2018 |
| 614FBE96-6355-4C3B-985B-002E2B9853CB | 646B5A64-EA28-4471-8964-0017EB511797 | Avocado | Test | 10/11/2018 |
| 74352B1B-36E8-4913-898E-002ED4CB21AD | 646B5A64-EA28-4471-8964-0017EB511797 | Tomato | Test | 10/11/2018 |
+--------------------------------------+--------------------------------------+---------+-------------------------+------------------------+
When I receive that info in c# from stored procedure like
SELECT * FROM Category AS c
INNER JOIN Product AS P ON C.CategoryId = P.CategoryId
So, I want to create groupbox dynamically. How can I create this groupbox depending on categories and their products? Regards
At the final I want something like this:
Update
I try as comments bellow like:
Receive data from datatable:
var data = db.GetTableBySQL("usp_RedMarks_Get");
foreach (DataRow itm in data.Rows)
{
if (itm["CategoryName"].ToString() != CurrentGroupBoxName)
{
flpRedMarks.Controls.Add(GetGroupBox(itm, 200, 100));
}
}
Create GroupBox and items:
private GroupBox GetGroupBox(DataRow c, int width, int height)
{
GroupBox currentGroupBox = new GroupBox();
currentGroupBox.Size = new Size(width, height);
currentGroupBox.Text = c["CategoryName"].ToString();
currentGroupBox.Name = c["CategoryName"].ToString();
CurrentGroupBoxName = currentGroupBox.Name;
var y = 20;
foreach (var itm in c.Table.Rows)
{
CheckBox cb = new CheckBox();
cb.Text = itm.ToString();
cb.Location = new Point(5, y);
// you can add an event here...
//cb.CheckedChanged += cb_SomeEvent;
currentGroupBox.Controls.Add(cb);
y += 20;
}
return currentGroupBox;
}
I receive data from SQL like:
But instead of get items(products) based in categories I get merged data:
What am I doing wrong? Regards
I would use flowLayoutPanel control to make the spacing for the groupboxes.
Then you can write a logic that will create your groupbox elements dynamically.
Here is one way to achieve this task:
namespace WindowsFormsApplication47
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var dataFromDb = GetData();
foreach (var itm in dataFromDb)
{
flowLayoutPanel1.Controls.Add(GetGroupBox(itm, 200, 100));
}
}
private GroupBox GetGroupBox(Category c,int width,int height)
{
GroupBox g = new GroupBox();
g.Size = new Size(width, height);
g.Text = c.Name;
var y = 20;
foreach (var itm in c.Items)
{
CheckBox cb = new CheckBox();
cb.Text = itm;
cb.Location = new Point(5, y);
// you can add an event here...
cb.CheckedChanged += cb_SomeEvent;
g.Controls.Add(cb);
y += 20;
}
return g;
}
private void cb_SomeEvent(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private List<Category> GetData()
{
// Just to simulate a database...
Category c1 = new Category("Fruit", new List<string>() { "Banana", "Apple" });
Category c2 = new Category("Vegetables", new List<string>() { "Avocado", "Tomato" });
Category c3 = new Category("Programming Languages", new List<string>() { "C#", "Visual Basic" });
Category c4 = new Category("Stars", new List<string>() { "Venus", "Mars" });
List<Category> result = new List<Category>();
result.Add(c1);
result.Add(c2);
result.Add(c3);
result.Add(c4);
return result;
}
}
class Category
{
public string Name;
public List<string> Items;
public Category(string name,List<string> items)
{
this.Name = name;
this.Items = items;
}
}
}
GUI OUTPUT:
UPDATE:
For OP's request, here is one way to achieve this without using List<T>.
In this example we are iterating straightforwardly on the DataTable (after we extracted the categories distinctly) and extracting the matched sub categories for each category using LINQ.
Please see comments inside the code:
private void Form1_Load(object sender, EventArgs e)
{
DataTable dataFromDb = GetData();
// extract distinct categories into an array - for each group box
string[] distinctCetgories = dataFromDb.AsEnumerable()
.Select(x => x.Field<string>("CategoryName")).Distinct().ToArray();
// iterate on the distinct categories
foreach(var itm in distinctCetgories)
{
// extract all the subcategories for the checkboxes inside the groupbox
string[] subcategories = dataFromDb.AsEnumerable().
Where(x => x.Field<string>("SubCategoryName") == itm).
Select(y => y.Field<string>("SubCategoryName")).ToArray();
flowLayoutPanel1.Controls.Add(GetGroupBox(itm,subcategories, 200, 100));
}
}
private GroupBox GetGroupBox(string categoryName,string[] subCategories,int width, int
{
GroupBox g = new GroupBox();
g.Size = new Size(width, height);
g.Text = categoryName;
var y = 20;
foreach (var itm in subCategories)
{
CheckBox cb = new CheckBox();
cb.Text = itm;
cb.Location = new Point(5, y);
// you can add an event here...
cb.CheckedChanged += cb_SomeEvent;
g.Controls.Add(cb);
y += 20;
}
return g;
}
private void cb_SomeEvent(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private DataTable GetData()
{
// return datadata using your query:
// SELECT* FROM Category AS c
// INNER JOIN Product AS P ON C.CategoryId = P.CategoryId
// ..... create datatable ......
return new DataTable();
}
If you can add and ORDER BY clause to your stored procedure like this:
SELECT C.Name AS 'CategoryName', P.Name AS 'SubcategoryName'
FROM Category AS C
INNER JOIN Product AS P ON C.CategoryId = P.CategoryId
ORDER BY C.Name
You can then iterate through the DataRows you get back like below. Note that I'm adding a flow panel inside the group box to avoid the need to keep an offset variable.
GroupBox currentGroupBox = new GroupBox();
FlowLayoutPanel currentGroupBoxPanel = null;
foreach (DataRow itm in _data.Rows)
{
var currentCategoryName = itm["CategoryName"].ToString();
if (currentCategoryName != currentGroupBox.Name)
{
currentGroupBox = new GroupBox();
currentGroupBox.Name = currentCategoryName;
currentGroupBoxPanel = new FlowLayoutPanel();
currentGroupBoxPanel.FlowDirection = FlowDirection.TopDown;
currentGroupBoxPanel.Dock = DockStyle.Fill;
currentGroupBox.Controls.Add(currentGroupBoxPanel);
flpRedMarks.Controls.Add(currentGroupBox);
}
var cb = new CheckBox();
cb.Text = itm["SubcategoryName"].ToString();
currentGroupBoxPanel.Controls.Add(cb);
}

Splitting hierarchies from datatable

I have a datatable which contains multiple hierarchies with different heights which I need to split.
eg.
|---------------------|------------------|
| Account | Hierarchy Account|
|---------------------|------------------|
| 1 | |
|---------------------|------------------|
| 2 | 1 |
|---------------------|------------------|
| 3 | 1 |
|---------------------|------------------|
| 4 | 2 |
|---------------------|------------------|
| 5 | 3 |
|---------------------|------------------|
| 6 | |
|---------------------|------------------
| 7 | 6 |
|---------------------|------------------|
| 8 | 6 |
|---------------------|------------------|
Below is what I have tried so far.
private List<DataTable> SplitDataTablesOnHierarchy(DataTable dataTable)
{
List<DataTable> dataTablesList = new List<DataTable>();
List<string> listTemp = new List<string>();
var HierarchyAccounts = dataTable.AsEnumerable().Where(m => m.Field<string>("Hierarchy Account Number") == "");
foreach(var topAccount in TopAccounts )
{
//Check if account exists in Hierarchy Account Number
var topAccountExists = dataTable.AsEnumerable().Any(m => m.Field<string>("Hierarchy Account Number") == topAccount.Field<string>("Account Number"));
if (topAccountExists == true)
{
DataTable newDataTable = dataTable.Clone();
newDataTable.ImportRow(payerAccount);
dataTablesList.Add(newDataTable);
}
//Top Accounts found and added to tempList
}
//CreateDataTable with Top Accounts
foreach(DataTable dTable in dataTablesList)
{
bool bottomHierarchyReached = true;
var TempSearch = dTable.Rows;
while(bottomHierarchyReached)
{
foreach(DataRow account in TempSearch)
{
var rows = dataTable.AsEnumerable().Where(m => m.Field<string>("Hierarchy Account Number") == account.Field<string>("Account Number")).CopyToDataTable();
if(rows.Rows.Count == 0)
{
bottomHierarchyReached = false;
break;
}
else
{
TempSearch = rows.Rows;
dTable.Rows.Add(rows.Rows);
}
}
}
}
return dataTablesList;
}
My thought process above was to first find the highest accounts in the hierarchy, create new datatables with those accounts and then drill down and add the following levels to the relevant datatable recursively since I do not know the height of each hierarchy.
Found a solution by creating a tempList which keeps the all of the lower levels while searching through the level above.
Once the loop through the SearchList is done we assign the tempList to it.
And then search through the next level of the hierarchy.
foreach (DataTable dTable in dataTablesList)
{
bool bottomHierarchyReached = true;
var SearchList = dTable.AsEnumerable().Select(p=> new { HierarchyAccount = p.Field<string>("Hierarchy Account Number"),
Account = p.Field<string>("Account Number")
}).ToList();
var tempList = SearchList.ToList();
tempList.Clear();
while (bottomHierarchyReached)
{
tempList.Clear();
foreach (var account in SearchList)
{
var rows = dataTable.AsEnumerable().Where(m => m.Field<string>("Hierarchy Account Number") == account.Account);
if(rows.Count() == 0)
{
bottomHierarchyReached = false;
break;
}
else
{
tempList.AddRange(rows.AsEnumerable().Select(p => new {
HierarchyAccount = p.Field<string>("Hierarchy Account Number"),
Account = p.Field<string>("Account Number")
}).ToList());
foreach(var row in rows)
{
dTable.ImportRow(row);
}
}
}
SearchList = tempList.ToList();
}
}

Replace values of List<Class>

I have two List<class>, List1 and List2 which contains multiple columns: RowNo, Value1, Value2, etc. as follows
List1
| RowNo | Value |
|-------|-------|
| 1 | 11 |
| 2 | 22 |
| 3 | 33 |
| 4 | 88 |
List2
| RowNo | Value |
|-------|-------|
| 1 | 44 |
| 2 | 55 |
| 3 | 66 |
I want to replace the value of element of List1 with the value of element of List2 if the RowNo matches.The output I want to generate is as follows
Desired result
| RowNo | Value |
|-------|-------|
| 1 | 44 |
| 2 | 55 |
| 3 | 66 |
| 4 | 88 |
Any Ideas or suggestions? How can I achieve this? What can be the best and efficient way to do this?
You can just use a loop to compare the values in List1 with List2, and if a match is found, update the Value
foreach (var item in List1)
{
var match = List2.FirstOrDefault(x => x.RowNo == item.RowNo);
if (match != null)
{
item.Value = match.Value;
}
}
Using Linq
List1.ForEach(l1 => l1.Value = (List2.FirstOrDefault(l2 => l2.RowNo == l1.RowNo) ?? l1).Value);
The Value property of l1 list element will be set to itself if no element will be found on the List2 list.
Full code
class MyClass
{
public int RowNo { get; set; }
public int Value { get; set; }
}
var List1 = new List<MyClass>()
{
new MyClass(){RowNo = 1, Value = 11},
new MyClass(){RowNo = 2, Value = 22},
new MyClass(){RowNo = 3, Value = 33},
new MyClass(){RowNo = 4, Value = 88},
};
var List2 = new List<MyClass>()
{
new MyClass(){RowNo = 1, Value = 44},
new MyClass(){RowNo = 2, Value = 55},
new MyClass(){RowNo = 3, Value = 66}
};
List1.ForEach(l1 => l1.Value = (List2.FirstOrDefault(l2 => l2.RowNo == l1.RowNo) ?? l1).Value);
List1.ForEach(x =>
{
var item = List2.FirstOrDefault(y => y.RowNo == x.RowNo);
if (item != null)
{
x.Value = item.Value;
}
});
Put all data of list1 into a Dictionary (key is the RowNo).
Loop over list2 to update the Dictionary.
Convert the data of the Dictionary to a List.
It approaches an O(n) operation.
Use this extension method to achieve what you want:
public static class LinqExtentions
{
public static void Project<T>(this IEnumerable<T> lst1, IEnumerable<T> lst2,
Func<T, object> key, Action<T, T> action)
{
foreach (var item1 in lst1)
{
var item2 = lst2.FirstOrDefault(x => key(x).Equals(key(item1)));
if (item2 != null)
{
action(item1, item2);
}
}
}
}
then you can use it like this:
List1.Project(List2, x => x.RowNo, (y, z) => { y.Value = z.Value; });
What it does
is projecting one list over the other, then matching the key values in both (RowNo in your example), when two items have the same key then the action supplied in the third parameter is applied, in this example, you want elements in the first list to have the same Value as elements in the second list, that's exactly what this delegate does:
(y, z) => { y.Value = z.Value; }
you can use this extension method to achieve the same requirement for any pair of lists:
Call Project on the list you want to change.
Pass the the list of the values you want to assign to the first list, as the first parameter.
Pass the key property as the second parameter.
The third
parameter is the action you want to apply on your list.
You can loop over List1 and check if List2 contains a match then fill the result in a new list
List<YourClass> result = new List<YourClass>();
for (int i = 0; i < List1.Count; i++)
{
YourClass resRowValue = List1[i];
if (List2.Count > i && List2[i].RowValue.equals(resStrRowValue.RowValue)
resStr.RowValue = List2[i].RowValue;
result.Add(resRowValue);
}
//set the result to List1
List1 = result;
You can do this also using linq
List1 = List1.Select(x => {
int i = List1.IndexOf(x);
YourClass newValue = List2.FirstOrDefault(y => y.RowValue.Equals(x.RowValue));
if (newValue != null)
x.RowValue = newValue.RowValue;
return x;
}).ToList();

Transform SQL Records to Object

Currently I am developing an application like SharePoint and I am encountering a difficult as followed.
I have a DB table to keep my contents like the following
+----+---------------+----------+---------+----------+--------------------------+
| ID | Content_type | List_ID | COL_ID | ITEM_ID | VALUE |
+----+---------------+----------+---------+----------+--------------------------+
| 1 | "Column" | 1 | 0 | | "ABC" |
| 2 | "Column" | 1 | 1 | | "DEF" |
| 3 | "Item" | 1 | 0 | 1 | "<VALUE OF Column ABC>" |
| 4 | "Item" | 1 | 1 | 1 | "<VALUE OF Column DEF>" |
+----+---------------+----------+---------+----------+--------------------------+
and I would like to display these record on the web using linq and C# like the following....
ITEM_ID |ABC |DEF
------------+---------------------+----------------------
1 |<VALUE OF Column ABC>|<VALUE OF Column DEF>
EDITED:
My questions are:
I would like to use the DB record stated as Column in the content_type field to be the DataColumn of a DataTable.
I would like to map all records in the DB stated as ITEM with the same Item_ID as 1 DataRow of a DataTable. The value field of each DB records will fall onto the column of the DataTable based on the Column ID.
Thanks for the help. I had make it by myself....
First get the records from DB where content_type = "Column" and use
these records to form a DataTable
Get all records from DB where content_typ= "Item" and re-group each item to a List where ITEM_id =
Map the Item.title = column of the DataTable and Items.value = value of the DataTable rows....
public static DataTable PopulateRec(int list_id, string web_app, string host_auth)
{
DataTable dt = new DataTable();
List Column = Get_Column(list_id, web_app, host_auth);
for (int i = 0; i < Column.Count(); i++)
{
DataColumn datacolumn = new DataColumn();
datacolumn.ColumnName = Column[i].ToString();
dt.Columns.Add(datacolumn);
}
List Items = Get_Item(list_id, web_app, host_auth);
if (Items.Count != 0)
{
int ItemCount = Convert.ToInt32((from Itms in Items
select Itms.Item_id).Max());
for (int j = 0; j <= ItemCount; j++)
{
dt.Rows.Add();
List IndvItem = (from Indv in Items
where Indv.Item_id == j
select Indv).ToList();
foreach (var val in IndvItem)
{
dt.Rows[j][val.title] = val.value;
}
IndvItem = null;
}
for (int k = 0; k < dt.Rows.Count; k++)
{
if (dt.Rows[k][0].ToString() == string.Empty)
{
dt.Rows[k].Delete();
}
}
}
Column = null;
Items = null;
return dt;
}
private static List Get_Column(int list_id, string web_app, string host_auth)
{
List content_db = new List();
List columnname = new List();
Config_DB_Context configdb = new Config_DB_Context("dms_config");
content_db = (from c in configdb.site_mapping
where c.host_auth == host_auth
&& c.web_app == web_app
select c.content_db).ToList();
for(int i = 0; i < content_db.Count(); i++)
{
Content_DB_Context contentdb = new Content_DB_Context(content_db[i]);
columnname = (from c in contentdb.content
where c.content_type == "Column"
&& c.list_id == list_id
select c.title).ToList();
}
content_db = null;
return columnname;
}
private static List Get_Item(int list_id, string web_app, string host_auth)
{
List content_db = new List();
List Itm = new List();
Config_DB_Context configdb = new Config_DB_Context("dms_config");
content_db = (from c in configdb.site_mapping
where c.host_auth == host_auth
&& c.web_app == web_app
select c.content_db).ToList();
for (int i = 0; i < content_db.Count(); i++)
{
Content_DB_Context contentdb = new Content_DB_Context(content_db[i]);
Itm = (from c in contentdb.content
where c.content_type == "Item"
&& c.list_id == list_id
select new MyItem
{
col_id = (int)c.column_id,
list_id = (int)c.list_id,
title = c.title,
value = c.value,
Item_id = (int)c.item_id,
hidden = c.hidden
}).ToList();
}
content_db = null;
return Itm;
}

Linq Union does not work

I have a two lists rca and purchase as follow.
List<GroupDate> rca = (from sold in GetSoldOut
group sold by new { sold.CreatedDate, sold.SubCategoryID }
into g
select new GroupDate
{
Date = g.Key.CreatedDate,
SubCategoryID = g.Key.SubCategoryID,
Count = g.Count()
}).ToList();
and
List<GroupDate> purchase = (from sold in stock
group sold by new { sold.CreatedDate, sold.SubCategoryID }
into g
select new GroupDate
{
Date = g.Key.CreatedDate,
SubCategoryID = g.Key.SubCategoryID,
Count = g.Sum(a => a.Quantity)
}).ToList();
And Join this two lists as follow.
var leftOuterJoinRP = from first in replace
join last in prepaid
on new { first.Date, first.SubCategoryID } equals new { last.Date, last.SubCategoryID }
into temp
from last in temp.DefaultIfEmpty(new GroupDate { })
select new CardBalance
{
Date = first.Date,
SubCategoryID = first.SubCategoryID,
ReDemage = first.Count,
Prepaid = last.Count
};
var rightOuterJoinRP = from last in prepaid
join first in replace
on new { last.Date, last.SubCategoryID } equals new { first.Date, first.SubCategoryID }
into temp
from first in temp.DefaultIfEmpty(new GroupDate { })
select new CardBalance
{
Date = last.Date,
SubCategoryID = last.SubCategoryID,
ReDemage = first.Count,
Prepaid = last.Count
};
leftOuterJoinRP contains
Date---| Balance | OpeningStock | Prepaid | Purchase | RCA | Demage | SubCategoryId
1/1/17 | 0-------| 0----------- | 1------ | 600 -----| 2-- | 0 ---- | 84
and
rightOuterJoinRP contains
Date---| Balance | OpeningStock | Prepaid | Purchase | RCA | Demage | SubCategoryId
1/1/17 | 0-------| 0----------- | 1------ | 600-----| 2-- | 0 ---- | 84
1/2/17 | 0-------| 0----------- | 1------ | 110-----| 1-- | 0 ---- | 84
Union leftOuterJoinRP and rightOuterJoinRP as follow.
var fullOuterJoinRP = leftOuterJoinRP.Union(rightOuterJoinRP);
But it does not union. fullOuterJoinRP get all rows.
You need to use the Union method which takes an IEqualityComparer<T> parameter.
Let's say you have a TestClass
public class TestClass
{
public int TestInteger { get; set; }
public string TestString { get; set; }
}
And create two lists
List<TestClass> list1 = new List<TestClass>();
list1.Add(new TestClass() { TestInteger = 1, TestString = "t1" });
list1.Add(new TestClass() { TestInteger = 2, TestString = "t2" });
List<TestClass> list2 = new List<TestClass>();
list2.Add(new TestClass() { TestInteger = 1, TestString = "t1" });
list2.Add(new TestClass() { TestInteger = 3, TestString = "t3" });
IEnumerable<TestClass> list3 = list1.Union(list2);
Here, the Union method will return all four objects, like in your question.
The Union method needs an IEqualityComparer<TestClass> parameter to compare the objects.
public class TestClassComparer : IEqualityComparer<TestClass>
{
public bool Equals(TestClass x, TestClass y)
{
//Check whether the objects are the same object.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether the class properties are equal.
return x != null && y != null && x.TestInteger.Equals(y.TestInteger) && x.TestString.Equals(y.TestString);
}
public int GetHashCode(TestClass obj)
{
//Get hash code for the TestString field if it is not null.
int hashTestString = obj.TestString == null ? 0 : obj.TestString.GetHashCode();
//Get hash code for the TestInteger field.
int hashTestInteger = obj.TestInteger.GetHashCode();
//Calculate the hash code for the TestClass object.
return hashTestString ^ hashTestInteger;
}
}
Now, if you call
IEnumerable<TestClass> list3 = list1.Union(list2, new TestClassComparer());
The resulting list3 will have three unique objects.

Categories

Resources