Comparing Two Queries and Synchronizing Results - asp.net

I have a table (Rooms) which contains a number of rows. Each row represents a room and each room needs to exist twice (once for fall, once for spring semester). Sometimes when folks add a room they only add it for one semester. I'm working on a process that will synchronize the rooms between semesters.
First I've pulled two queries, one that gets all of the rooms with fall in their semester column and one that gets all of the rooms with spring in their semester column, like so:
Dim getFallRooms = (From p In dbContext.Rooms _
Where p.semester = "Fall" _
Select p)
Dim getSpringRooms = (From p In dbContext.Rooms _
Where p.semester = "Spring" _
Select p)
The results will each contain multiple rows with the following columns: id, building, room, occupant, and semester.
What I want to do is something like this (pseudo):
For Each row in getFallRooms
If row.building and row.room not in getSpringRooms Then
' Code to add new row with fall as the semester.
End If
Next
Any suggestions on how I can make this into actual, workable code?

For Each row in getFallRooms
If (From p in getSpringRooms
Where p.building = row.building
AndAlso p.room = row.room).Count = 0 Then
' Code to add new row with fall as the semester.
End If
Next

You could do something quite similar to what's being done at this link.
I don't know precisely how to do it in VB.NET, but here's how I would do it in C#, using the LINQ extension methods:
var fallRooms = dbContext.Rooms.Where(room => room.semester.Equals("Fall")).Select(r => r.Name);
var nonSpringRooms = dbContext.Rooms.Where(room => room.semester.Equals("Spring"))
.Select(r => r.Name)
.AsQueryable()
.Except(fallRooms);
Afterwards, you could then do a For Each loop on nonSpringRooms to do whatever it is that you'd want to do.
On a side note, someone should correct me if I'm wrong, but I believe that the above code would only go to the database once. Hence, you'd get that benefit as well.
EDIT: I realized that since they'd be on the same table that the records would have the same primary key. I've subsequently changed the query above, assuming that you have a name field that would be the same in both records.

Related

Use linq in the where clause

I have 2 tables _customerRepository.GetAllQueryable() and _customerSettlementRepository.GetAllQueryable().
In table _customerSettlementRepository.GetAllQueryable(), I have column ApplyD (date), after joining these two together, I want to find out max ApplyD in the where clause. This is my code:
var settlements = from c in _customerRepository.GetAllQueryable()
join cs in _customerSettlementRepository.GetAllQueryable() on new {c.CustomerMainC, c.CustomerSubC}
equals new {cs.CustomerMainC, cs.CustomerSubC} into c1
where cs.ApplyD == (c1.Select(b=>b.ApplyD).Max())
select new CustomerSettlementViewModel()
{
TaxRate = cs.TaxRate
};
It's remarkable that quite often in these questions people come up with an SQL(-like) statement without specification of the goal they want to reach. Hence it is impossible to see whether the provided statement fulfills the requirements.
Anyway, it seems you have something like Customers (in CustomerRepository) and CustomerSettlements in CustomerSettlementRepository.
both Customers and CustomerSettlements have a CustomerMainC and a CustomerSubC. You want to join Customers and CustomerSettlements on these two properties.
A CustomerSettlement also has an ApplyD and a TaxRate.
You only want to keep the join results where ApplyD has the maximum value of ApplyD
Finally, from every remaining join result you want to create one CustomerSettlementViewModel object with the value of the TaxRate in the join result that was taken from the CustomerSettlement.
Now that I wrote this, it baffles me why you need to join in the first place, because you only use values from the CustomerSettlements, not from the Customer.
Besides, if two Customers are joined with the same CustomerSettlements. this will result in two equal CustomerSettlementViewModel objects.
But let's assume this is really what you want.
In baby steps:
IQueryable<Customer> customers = ...
IQueryable<CustomerSettlement> customerSettlements = ...
var joinResults = customers.Join(customerSettlements
customer => new {customer.CustomerMainC, customer.CustomerSubC},
settlement => new {settlement.CustomerMainC, settlement.CustomerSubC}
(customer, settlement) => new
{
settlement.ApplyD,
settlement.TaxRate,
// add other properties from customers and settlements you want in the end result
});
In words: take all Customers and all CustomerSettlements. From every Customer create an object having the values of the customer's CustomerMainC and CustomerSubC. Do the same from every CustomerSettlement. When these two objects are equal, create a new object, having the values of the CustomerSettlement's ApplyD and TaxRate (and other properties you need in the end result)
Note that this is still an IQueryable. No query is performed yet.
From this joinResult you only want to keep those objects that have the value of ApplyD that equals the maximum value of ApplyD.
This question on StackOverflow is about selecting the records with the max value. The idea is to group the records into groups with the same value for ApplyD. Then order the groups in descending Key order and take the first group.
var groupsWithSameApplyD = joinResults.GroupBy(
joinedItem => joinedItem.ApplyD,
joinedItem => new CustomerSettlementViewModel()
{
TaxRate = orderedItem.TaxRate,
// add other values from joinedItems as needed
});
Every group in groupsWithSameApplyD has a key equal to ApplyD. The group consists of CustomerSettlementViewModel objects created frome the joinedItems that all have the same ApplyD that is in the Key of the group.
Now order by descending:
var orderedGroups = groupsWithSameApplyD.OrderByDescending(group => group.Key);
The first group contains all elements that had the largest ApplyD. Your desired result is the sequence of elements in the group.
If there is no group at all, return an empty sequence. Note if a sequence is requested as result, it is always better to return an empty sequence instead of null, so callers can use the returned value in a foreach without having to check for null return
var result = orderedGroups.FirstOrDefault() ??
// if no groups at all, return empty sequence:
Enumerable.Empty<CustomerSettlementViewModel>();
Note: the FirstOrDefault is the first step where the query is actually performed. If desired you could put everything in one big query. Not sure if this would improve readability and maintainability.
This is my syntet error, I need to write this since the first
var settlements = from c in _customerRepository.GetAllQueryable()
join cs in _customerSettlementRepository.GetAllQueryable() on new {c.CustomerMainC, c.CustomerSubC}
equals new {cs.CustomerMainC, cs.CustomerSubC}
select new CustomerSettlementViewModel()
{
TaxRate = cs.TaxRate
};
settlements = settlements.Where(p => p.ApplyD == settlements.Max(b => b.ApplyD));

ASP.NET returns incorrect data from oData Controller

I have an ASP.NET Web API app using Oracle's Entity Framework driver. I have an entity defined for a view as follows:
CREATE OR REPLACE FORCE VIEW "PHASE_TWO"."EDIPRODUCT" ("ID", "STK_NUM", "TITLE", "ISBN", "UPC", "ITEMNO", "LONGFORMAT", "ABRIDGED", "WEB_TITLES_ID", "OCLC", "GENRE", "RELYEAR", "ORIG_REL", "LANG", "ORIG_STKNUM", "PUBLISHER", "PEOPLELIST", "SALES_ORG", "NOT_AVAIL") AS
SELECT sap_product.id,
sap_product.stk_num,
sap_product.longdesc AS title,
sap_product.isbn,
sap_product.upc,
sap_product.itemno,
sap_product.longformat,
sap_product.abridged,
mwt_product.web_titles_id,
mwt_product.oclc,
mwt_product.genre,
mwt_product.RELYEAR,
sap_product.orig_rel,
sap_product.lang,
sap_product.orig_stknum,
UPPER (publisher.name) publisher,
(SELECT LISTAGG (p.FULLNAME, ', ') WITHIN GROUP (
ORDER BY pp.rank) AS People
FROM people p
JOIN product_people pp
ON p.id = pp.peopleid
WHERE pp.stk_num = sap_product.stk_num
GROUP BY pp.STK_NUM
) PeopleList,
sppg.PRICING_TYPE as sales_org,
sap_product.not_avail
FROM sap_product
JOIN mwt_product ON sap_product.stk_num = mwt_product.stk_num
JOIN publisher ON mwt_product.publisherid = publisher.id
JOIN SAP_PRODUCT_PRICING_GROUP sppg on sppg.STK_NUM = mwt_product.stk_num and sppg.MARKED_FOR_DELETION = 0
WHERE mwt_product.WEB_PRODUCTS_ID > 0;
This view works as expected in SQL Developer. My getEDIPRODUCT function (yes, it's VB.NET) in my controller is as follows:
' GET: odata/EDIPRODUCTs
<EnableQuery>
Function GetEDIPRODUCT() As IQueryable(Of EDIPRODUCT)
Dim results As IQueryable
results = db.EDIPRODUCT
For Each _product In results
Console.Write(_product)
Next
Return results
End Function
I just added the for loop in order to inspect the results. What I see when I inspect the results is the same product record is returned for each row. The value for the ID is duplicate and the only other field that should have variant values (sppg.PRICING_TYPE as sales_org) also just repeats.
I have other views where this does not occur. The correct number of records are always returned, but the first record retrieved is always just repeated in each row of the result set. Any idea what could be going on here?
I never actually resolved this issue and am still interested in why this fails, but I rewrote the portion of the app that uses this view to use OData's $expand to retrieve the related data.

Remove Matching List(of Object) from Another List(of Object) using LINQ

I am having a bit of a difficult time figuring out how accomplish a task outlined in my question header.
Basically, I have a list of 'News' objects defined as:
Dim news_list As List(Of News) = myNamespcae.News.ListNews()
Depending on a condition, I have another 'News' object list as;
Dim news_headlines As List(Of News) = myNamespace.News.getHeadlines()
Then, I have 'spots' again as List(of News) as;
Dim spots = (From n In news_list Take (10) Select n)
I am trying to accomplish;
if news_headlines is not empty,if any news_headlines News object exists in spots, remove it from spots. return filtered spots.
Any guidance will be appreciated..
Thanks.
If you want to pick ten from Spots and then filter out the ones in news_headlines, it would be something like this (warning: LINQ syntax in VB entirely from memory):
Dim spots1 = (From n In news_list Take (10) Select n)
Dim spots2 = (From n in spots1 Where Not news_headlines.Contains(n) Select n)
If you want to filter out the ones in news_headlines and then pick ten, it would be something like this:
Dim spots1 = (From n In news_list Where Not news_headlines.Contains(n) Select n)
Dim spots2 = (From n in spots1 Take (10) select n)
You can of course combine the two queries. Note that I'm assuming news_headlines is not null. I'm also assuming that your news items are either the same object instances or implement IEquatable<T>.
I think there are similar questions in stackoverflow. Anyway, you can possibly use "Intersect" to get the elements that are both in the spots and the headlines and remove them from the spots list.
The code should be trivial but I haven't used VB.net in a while, sorry.

Using a Repeater with a dynamically generated table, ie, so unknown field names

I'm trying to produce a repeater showing amounts of money taken by various payment types into a table.
Payment types available come from a global settings file as an array, I am creating a dataTable by looping this list and extracting sales reports (there might be a more efficient way than this loop, but this is not my concern at the minute).
My question: How do I bind this to a repeater and display it when I dont necessarily know the table column names?
I've tried various methods to give the table a header row and give the columns numerical names from a for > next loop, but am either getting no results, or
System.Data.DataRowView' does not contain a property with the name '7'. < or whatever number
This is where I currently am:
EDIT: JUST REALISED MY CODE WAS AWFUL, SO UPDATED:
Dim paymentTable As New DataTable("paymentTable")
For j = 0 To UBound(paymentTypes)
Dim Type = Trim(paymentTypes(j))
Dim headers As DataColumn = New DataColumn(j.ToString)
paymentTable.Columns.Add(headers)
Next
Dim titleRow As DataRow = paymentTable.NewRow()
For k = 0 To UBound(paymentTypes)
Dim Type = Trim(paymentTypes(k))
titleRow.Item(k) = Type
Next
paymentTable.Rows.Add(titleRow)
Dim newRow As DataRow = paymentTable.NewRow()
For i = 0 To UBound(paymentTypes)
Dim Type = Trim(paymentTypes(i))
Try
newRow.Item(i) = '' GO OFF AND GET STUFF FROM DB
Catch
newRow.Item(i) = "0 "
End Try
Next
paymentTable.Rows.Add(newRow)
THIS EDITED CODE WORKS BUT I ONLY GET ONE ITEM
What I was hoping for would look something like:
card | cash | paypal ... etc (headings row)
£250 | £54 | £78 ... etc (values row)
Obviously there're a million ways this can be done, but this makes sense for my application, which has to be expandable and contractable depending on payment types available and this whole table needs to be repeated for multiple locations (also variable depending on who's viewing, and the number of locations in the system)
No, dont give up but just dont name columns by absolute number with no string before try
Dim headers As DataColumn = New DataColumn("col"+ j.ToString)

please help me in creating a sort expressions for datatable in vb.net

I have got a datatable which contains many columns in which three are main:
hotelid
dshotelid
hotelname
Some hotels contain only dshotelid, some contains only hotelid and some contain both dshotelid and hotelid.
I need sort in such way so that those hotels who got both dshotelid and hotelid should be on top and then those hotels who have got only dshotelid and at last those hotels who have got only hotelid...
Please help me to create such a sort expression.
I created this :
dtmaintable.DefaultView.Sort = "dshotelid, hotelid"
but it Is not giving me desired output.
Fields with NULL in them tend to appear at the top of sorted columns (unless the sort is in descending order) so you're probably seeing rows with both hotelid and dshotelid at the bottom, instead of the top?
How is this data table populated? If it's coming from a database query, it's a simple matter to construct an additional column (or columns) which contain(s) whatever sort key you need - be it an amalgum of other columns or some other unique identifier.
EDIT: Feb 4, 2010 - in response to your 'FilterAndSortTable' solution:
Your solution works but it's not because of the FilterAndSortTable - it's because you used a different sort order.
Originally, you used "dsohtelid, hotelid".
The second time, you used "dshotelid desc, hotelid desc".
This has the effect of putting your non-nulls at the top and nulls at the bottom, but I would dispute that this qualifies as a good solution.
Your id's are now sorted backwards - which I kind of assumed you might want to avoid, hence my suggestion of a new sort column that would respect this.
Still, if the order of id's doesn't matter then your solution is fine and you can simply stick with your original code, with the addition of 'desc', like so:
dtmaintable.DefaultView.Sort = "dshotelid desc, hotelid desc"
Of course, if anything I've said here has been of any use to you, a tick would be muchly appreciated. It would help me reach 50 rep points and finally be able to write comments. :)
i got the answer its :
datagrid1.datasource = FilterandSortTable(dtmaintable,"", "dshotelid desc, hotelid desc")
Public Shared Function FilterandSortTable(ByVal SourceTable As DataTable, ByVal strFilters As String, Optional ByVal strOrder As String = "") As DataTable
Dim Tbl As DataTable = SourceTable.Clone
Dim rows() As DataRow = SourceTable.Select(strFilters, strOrder)
For i As Integer = 0 To rows.Length - 1
Tbl.ImportRow(rows(i))
Next
Return Tbl
End Function

Resources