Materialising "Friendly" Entities from a disconnected EF5 Context - vb.net

I have an application utilising Entity Framework 5 Database First, VB.net, Linq, SQL Server 2008 R2 and a Disconnected Repository Pattern.
I've used the EF5.x dbContext generator to create my POCO's and modified the T4 Template to add various extra bits and bobs, including my INotifiyPropertyChanged Event Raising.
I have various linked tables, and I need to show data in a "Friendly" manner.
As an example, I have the following two tables;
Colours:
Colour_ID | Colour_Name | Created_By | Creation_Date | Modified_By | Modification_Date
----------------------------------------------------------------------------------------------
1 | Blue | 1 | 22-01-13 | 1 | 23-01-13
Users:
User_ID | First_Name | Last_Name |
--------------------------------------
1 | Peter | Gallagher |
In order to show the "Friendly Data" in my DataGrids, I'm pulling in the data, and creating "Friendly Entities" using code such as;
Using CriticalPathDBContext As CriticalPathEntities = ConnectToDatabase()
Dim query = (From Colour In CriticalPathDBContext.Colours _
.Include(Function(x) x.CreationUser) _
.Include(Function(x) x.LastUpdateUser).AsNoTracking.ToList
Select New FriendlyColours With {.CreatedBy = If(Colour.CreationUserCode Is Nothing, "", Colour.CreationUser.First_Name & " " & Colour.CreationUser.Last_Name),
.CreationDate = Colour.CreationDate,
.CreationUserCode = Colour.CreationUserCode,
.LastUpdateDate = Colour.LastUpdateDate,
.LastUpdatedBy = If(Colour.LastUpdateUserCode Is Nothing, "", Colour.LastUpdateUser.First_Name & " " & Colour.LastUpdateUser.Last_Name),
.LastUpdateUserCode = Colour.LastUpdateUserCode,
.Colour_ID = Colour.Colour_ID,
.Colour_Name = Colour.Colour_Name}).OrderBy(Function(x) x.Colour_Name)
Return New ObservableCollection(Of FriendlyColours)(query)
End Using
My problem with the above is, for more complicated connected Entities, this type of query takes a too long.
I've considered using my Buddy Classes to pull in the related data, however in a disconnected pattern, this doesn't work for me.
Obviously, while disconnected, lazy loading isn't an option...
So, my question is... Is there a better way of doing this? For better... Read faster!
Any help would be gratefully received! Maybe I'm missing something obvious!
Edit...To further explain:
I have a query which returns all Products, as well as their
Components and Product Stock Items...
There are 4 different sorts of Component lists which are returned, as
well as a list of Product Stock Items.
I take each set of Components and Product Stock Items, and fill an
associated Grid with the results.
The User is able to add, edit or delete items from the various Components or Product Stock items.
The query is (sorry for the size!);
Dim query = (From ProductList In _DBContext.Products _
.Include(Function(x) x.CreationUser) _
.Include(Function(x) x.LastUpdateUser) _
.Include(Function(x) x.Colour) _
.Include(Function(x) x.License) _
.Include(Function(x) x.Pack_Size) _
.Include(Function(x) x.Customer) _
.Include(Function(x) x.Supplier) _
.Include(Function(x) x.ProductComponents) _
.Include(Function(x) x.ProductComponents.Select(Function(y) y.ApprovalType)) _
.Include(Function(x) x.ProductComponents.Select(Function(y) y.Component)) _
.Include(Function(x) x.ProductComponents.Select(Function(y) y.Component).Select(Function(z) z.ComponentType)) _
.Include(Function(x) x.Product_Stock_Item) _
.Include(Function(x) x.Product_Stock_Item.Select(Function(y) y.Pack_Type)) _
.Include(Function(x) x.Product_Stock_Item.Select(Function(y) y.Product_Sizing)) _
.Include(Function(x) x.Product_Stock_Item.Select(Function(y) y.Units_Of_Measure)) _
.Include(Function(x) x.ProductType) _
.Include(Function(x) x.ProductClassification) _
.Include(Function(x) x.ProductType.ProductDepts) _
.Include(Function(x) x.Season).AsNoTracking().OrderBy(Function(x) x.Product_Code).ToList
Select New FriendlyProducts With {.Colour_ID = ProductList.Colour_ID,
.Colour_Name = If(ProductList.Colour_ID Is Nothing, "", ProductList.Colour.Colour_Name),
.CreatedBy = If(ProductList.CreationUserCode Is Nothing, "", ProductList.CreationUser.First_Name & " " & ProductList.CreationUser.Last_Name),
.CreationDate = ProductList.CreationDate,
.CreationUserCode = ProductList.CreationUserCode,
.FriendlyCreationUser = ProductList.CreationUser,
.Cust_Product_Desc_24 = ProductList.Cust_Product_Desc_24,
.Cust_Product_Desc_48 = ProductList.Cust_Product_Desc_48,
.Customer_Code = ProductList.Customer_Code,
.Customer_Name = If(ProductList.Customer_Code Is Nothing, "", ProductList.Customer.NAME),
.Description = ProductList.Description,
.DesignNo = ProductList.DesignNo,
.Gender_ID = ProductList.Gender_ID,
.Gender_Name = If(ProductList.Gender_ID Is Nothing, "", ProductList.Gender.Gender_Name),
.LicenseCode = ProductList.LicenseCode,
.License_Name = If(ProductList.LicenseCode Is Nothing, "", ProductList.License.NAME),
.Pack_Size_ID = ProductList.Pack_Size_ID,
.Pack_Size_Name = If(ProductList.Pack_Size_ID Is Nothing, "", ProductList.Pack_Size.Pack_Size_Name),
.PackagingNR = ProductList.PackagingNR,
.ProductClassification_ID = ProductList.ProductClassification_ID,
.Product_Classification_Name = If(ProductList.ProductClassification_ID Is Nothing, "", ProductList.ProductClassification.ProductClassification_Name),
.Product_Code = ProductList.Product_Code,
.Product_Picture_Path = ProductList.Product_Picture_Path,
.ProductComponentsNR = ProductList.ProductComponentsNR,
.ProductType_ID = ProductList.ProductType_ID,
.ProductType_Name = If(ProductList.ProductType_ID Is Nothing, "", ProductList.ProductType.NAME),
.ProductDept_ID = If(ProductList.ProductType Is Nothing, Nothing, ProductList.ProductType.ProductDept),
.ProductDept_Name = If(ProductList.ProductType Is Nothing, "", (If(ProductList.ProductType.ProductDepts Is Nothing, "", ProductList.ProductType.ProductDepts.NAME))),
.SageDescription = ProductList.SageDescription,
.SeasonCode = ProductList.SeasonCode,
.Season_Name = If(ProductList.SeasonCode Is Nothing, "", ProductList.Season.NAME),
.StrikeOffNR = ProductList.StrikeOffNR,
.Supplier_Code = ProductList.Supplier_Code,
.Supplier_Name = If(ProductList.Supplier_Code Is Nothing, "", ProductList.Supplier.NAME),
.TransfersNR = ProductList.TransfersNR,
.Deleted = ProductList.Deleted,
.LastUpdateDate = ProductList.LastUpdateDate,
.LastUpdatedBy = If(ProductList.LastUpdateUserCode Is Nothing, "", ProductList.LastUpdateUser.First_Name & " " & ProductList.LastUpdateUser.Last_Name),
.LastUpdateUserCode = ProductList.LastUpdateUserCode,
.ProductComponents = If(ProductList.ProductComponents.Count > 0, GetProductComponentsByPrimaryName(ProductList.ProductComponents, "Component"), New ObservableCollection(Of FriendlyProductComponents)),
.ProductPackaging = If(ProductList.ProductComponents.Count > 0, GetProductComponentsByPrimaryName(ProductList.ProductComponents, "Packaging"), New ObservableCollection(Of FriendlyProductComponents)),
.ProductStrikeOffs = If(ProductList.ProductComponents.Count > 0, GetProductComponentsByPrimaryName(ProductList.ProductComponents, "Strike Off"), New ObservableCollection(Of FriendlyProductComponents)),
.ProductTransfers = If(ProductList.ProductComponents.Count > 0, GetProductComponentsByPrimaryName(ProductList.ProductComponents, "Transfers"), New ObservableCollection(Of FriendlyProductComponents)),
.ProductStockItems = If(ProductList.Product_Stock_Item.Count > 0, GetProductStockItems(ProductList.Product_Stock_Item), New ObservableCollection(Of FriendlyProductStockItems))
}).Where(Function(x) x.Deleted = False Or x.Deleted Is Nothing)
Where GetProductComponentsByPrimaryName calls a function which simply filters the Components by their type and returns a Friendly ObservableCollection.
So each related Set of Components and Product Stock Items is returned as an ObservableCollection, which I can interact with...
Sorry about the long post!
EDIT - 08-03-13:
I have not resolved this issue above, but I have managed to persuade the client that they shouldn't be retrieving all the results and then relying on the User to filter afterwards.
This also led me to rejig my Filtering routines so that the Filtering is performed on the database, rather than locally.
Both of these two factors mean the query below now functions at a reasonable speed.
I tried filling my entities manually, but this took far longer than the query which Linq produced for me.
One of these days perhaps I'll revisit this problem to see what I can learn. But for now, I've sidestepped it!

Create a stored procedure that does this same query and return the results as a new entity type or as FriendlyColours if you can swing it. You are right, doing it like this is a hog. Not sure what else I can add.

That Query will result in a massive amount of data over the wire. You should probably start by projecting to entities that only contains the data you need. Maybe that won't help here though depending on you data needs.
SQL is usually blazing fast at running simple queries so you could try to load each collection seperately and then Stich the entities toghether in code. This will grately reduce the amount of data transfared.
To explain why joining like this will kill your performance here is a simple example:
In my test database I have three tables with very little data but you should still see the pattern.
First the individual queries
SELECT * FROM [OPath-dev].[dbo].[Groups] g
1;Hästhovarna;nzb5x50vibb;0;NULL;NULL;0
One single row. 39 characters according to notepad++
SELECT * FROM [OPath-dev].[dbo].[GroupMemberships]
1;1;1
2;1;0
Two rows. 12 characters
SELECT * FROM [OPath-dev].[dbo].[Blogs] where id > 5
3 rows with the fields;
Title;
Intro;
Body;
PublishDate;
Created;
CreatorIP;
Creator;
Edited;
Editor;
EditorIP
at 5907 characters
Joining the two simple tables
SELECT *
FROM [OPath-dev].[dbo].[Groups] g
JOIN [OPath-dev].[dbo].[GroupMemberships] gm on gm.GroupId = g.Id
1;Hästhovarna;nzb5x50vibb;0;NULL;NULL;0;1;1;1
1;Hästhovarna;nzb5x50vibb;0;NULL;NULL;0;2;1;0
The length is now 96 characters. If running these two in separate queries it would be 39 + 12 = 51 (Which probably is faster because the small difference).
Joining all three tables
SELECT *
FROM [OPath-dev].[dbo].[Groups] g
JOIN [OPath-dev].[dbo].[GroupMemberships] gm on gm.GroupId = g.Id
JOIN [OPath-dev].[dbo].[Blogs] b on b.Id > 5
The response is 6 rows with the columns:
Id;
Name;
JoinCode;
IsCompetitionClub;
SourceSystemKey_SystemKey;
SourceSystemKey_EntityId;
SourceSystemKey_HasValue;
UserId;
GroupId;
IsAdministrator
ID;
Title;
Intro;
Body;
PublishDate;
Created;
CreatorIP;
Creator;
Edited;
Editor;
EditorIP
The response now is 11954 characters long and we are suddenly paying quite a toll for the joins. Especially if the database is located on the same machine or on a really fast network.
Now this is even a bad example. I have better datasets that would show even worse growth on my other computer. And remeber that you are joining many more columns which will end up in a massive amount of data transfered. If it's a windows application and you are connecting to the database over a WAN that will be a serious problem but even on the local machine you can see that the transfer is not free if you profile.

Related

Global Class for SQL enumerated objects

I'm working on a tool crib VB.NET project, my first real one, so I'm struggling with a few things. In my SQL database I have a few tables that translate into enumerated fields. For example I have code and code type tables. I have a code type with CodeTypeID = 2 and description = Tool Status. These are used to track the location of a tool; Shop, In Transit, On Site, Out of Service, etc. I've set up enumerations for some of these.
Enum CodeType
User = 1
ToolStatus = 2
Repair = 3
OutOfService = 4
End Enum
ToolStatus
Shop = 5
Reserved = 6
InTransit = 7
OnSite = 9
ReturnTransit = 10
OutOfService = 11
Repair = 12
End Enum
The problem is if I change a code in SQL I then have to edit my enumerations as well. In the SQL tables I am only storing the CodeID and join to the code table to get the description. Other than an enumeration, I haven't found a good way to load these tables when the project opens that will allow me to code as easily, for example ToolStatus.OnSite to return the number 9 to my SQL table. It just seems inefficient to me. I always try to code my SQL procedures so if something changes I don't have to go and edit a bunch of procedures and would like to do the same here, especially as a simple addition of a new code could force a version update. Is there a better way to do this? I have searched the Internet and haven't found anything that really works as well as enumeration.
Example Code:
In the following code both OnSite and ReturnTransit are enumerations.
I'm moving items between two datagridviews, a Source and a Target DGV. Also updating the underlying shared datatable.
Dim cbo As ComboBox = sender
With cbo
Select Case .Name
Case "cboPeeps"
dvgFilter = "N"
Case "cboToolbox"
For i As Integer = 0 To dtTools.Rows.Count - 1
dtTools.Rows(i)("dgvFilter") = dtTools.Rows(i)("dbFilter")
Next
If cbo.Items.Count > 0 Then
filter = "ToolboxID=" & .SelectedValue.ToString & " And ToolStatus=" & OnSite
filter += " And UserID=" & .SelectedItem("UserID").ToString
End If
dvgFilter = "T"
End Select
End With
End If
Dim result() As DataRow = dtTools.Select(filter)
For i As Integer = 0 To result.Count - 1
result(i)("dgvFilter") = dvgFilter
If result(i)("dgvToolStatus") <> OutOfService Then result(i)("dgvToolStatus") = ReturnTransit
Next
I was, of course, trying to make this way too difficult. Simple solution along these lines.
Public Class Crepair
Public repairID As Integer
Public techID As Integer
Public repairCodeID As Integer
Public acceptedDate As Date
Public remarks As String
Public isComplete As Boolean
End Class
With nRepair
.repairID = row.Cells("RepairID").Value
.techID = row.Cells("TechID").Value
.repairCodeID = row.Cells("RepairCodeID").Value
.acceptedDate = row.Cells("AcceptedDate").Value
.remarks = row.Cells("Remarks").Value
.isComplete = row.Cells("IsComplete").Value
End With

Linq query to display unique set of names for current data set

I have a linq query that is used to display the list of requests from multiple users.And a Requestor can have multple requests(So the grid can have same Requestor multiple times). Now i am creating a dropdown list with unique Requestor that are displayed on the grid.(issue is i am not getting the distinct list but getting all the Requestor multiple times). Below is the linq query i am unsing.Can anyone suggest with correct linq query.
Dim rqstQry = From x In db.Request_vws _
Order By x.RequestID Descending _
Select x.RequestID,
Descr = x.Descr, _
RequestorName = String.Format("{0} {1}", x.FIRST_NAME, x.LAST_NAME), _
RelatedTask = GetTaskDescr(x.WorkID, x.TaskLabel, x.TaskDescr), _
RequestDescr = GetRequestDescr(x.RequestType), x.SubmitDttm, x.UpdatedDttm, _
x.ChangeDttm, _
x.MigrTimeStr, x.MigrApptTime, _
x.Requestor Ditinct
DataBind:
RequestorCB1.DataSource = rqstQry
RequestorCB1.DataTextField = "Requestor" RequestorCB1.DataValueField = "REquestor"
RequestorCB1.DataBind()
Need distinct user in the dropdownlist
Put parentheses around the LINQ query and append .Distinct()
Dim rqstQry = (From x In db.Request_vws _
Order By x.user
Select x.user).Distinct()
If you are including Request stuff in the result, then you cannot get distinct users (as Gert Arnold points out in his comment). Only include columns related to users.
If you still need information on requests then you must limit this information to one record per user. You would use a group by and use an aggregate in order to select a request (the first one, the last one etc.).
Got this by using the below query.
Dim rqstQry = (From x In db.Request_vws _
Order By x.RequestID Descending _
Select x.RequestID,
Descr = x.Descr, _
RequestorName = String.Format("{0} {1}", x.FIRST_NAME, x.LAST_NAME), _
RelatedTask = GetTaskDescr(x.WorkID, x.TaskLabel, x.TaskDescr), _
RequestDescr = GetRequestDescr(x.RequestType), x.SubmitDttm, x.UpdatedDttm, _
x.ChangeDttm, _
x.MigrTimeStr, x.MigrApptTime, _
x.Requestor). Distinct()
Dim rqstQry2 = (From y In rqstQry _
Select y.Requestor, y.RequestorName).Distinct()

LINQ Join between datatables, two untyped fields

I'm querying two databases and trying to join the result sets and query out of them using LINQ. It seems it would be an easy task, but without doing an explicit join I'm having major performance issues. When I do the explicit join, I am having trouble with VB syntax for making things explicit types. Working Code, Cleaned:
For Each CurrRow In ResultsA.Tables(15).Rows
CurrDate = CurrRow("Date")
CurrID = CurrRow("ID")
CurrVal = CurrRow("Val")
Dim ResultsB = From SMW In DataSetA.Tables(0).AsEnumerable() _
Where SMW("ID") = CurrScheduleID And SMW("Time") = CurrProfileDate _
Select UTC_TS = SMW("Time"), Value = (SMW("VALUE") / 1000), Time_Zone = SMW("Time_Zone"), ID = SMW("ID")
Dim CurrentResult As Object
Dim boolSchedDateFound As Boolean = False
For Each CurrentResult In ResultsB
If CurrentResult.Value <> CurrVal Then
'LogIntegrityCheckErrorRow()
End If
boolSchedDateFound = True
Next
Next
This takes FOREVER to run with 100,000 rows.
I've been trying to rewrite this as:
Dim MismatchRows = From TableAData In DataSetA.Tables(0).AsEnumerable() Join TableBData In DataSetB.Tables(15).AsEnumerable() _
On New With {.TableAID = Convert.ToInt32(TableAData("ID")), .TableATime = Convert.ToDateTime(TableAData("Date"))} _
Equals New With {.TableBDID = Convert.ToInt32(TableBData("ID")), .TableBTime = Convert.ToDateTime(TableBData("Time"))} _
Select .................. (Hard to clean up, but this isn't the part that's failing)
And I'm having a bear of a time with it. The fundamental problem is the lack of strong typing. I've looked, but there seems to be little advice because most people doing this build EF on the data. Which isn't a terrible idea, but would require a bunch of re-engineering. So. Problem in front of me, how do I remove the error:
'Equals' cannot compare a value of type '<anonymous type> (line 2641)' with a value of type '<anonymous type> (line 2642)'.
Thank you so much for your help.
Have you tried this?
Dim MismatchRows = From TableAData In ei _
Join TableBData In e2 On _
TableAData("ID") Equals TableBData("ID") And TableAData("Data") Equals TableBData("Time")
Select .......
db.tb_DeviceGeFenceDetail.Join(db.tb_InventoryLog, Function(gfd) gfd.DeviceID, Function(il) il.deviceId, Function(gfd, il) New From { _
gfd.GeFenceLat1, _
gfd.GeFenceLng1, _
gfd.GeFenceLat2, _
gfd.GeFenceLng2, _
il.deviceName, _
il.DeviceIcon, _
gfd.DeviceID, _
il.id _
}).ToList().Where(Function(y) intValues.Contains(y.id))
you can try joining tables something like this.

linq query dynamically add where condition?

I want to filter records dynamically on user choice. The user may choose to show the debit amount greater than credit or credit amount greater than debit. Accordingly I want to put a condition similar to either totDebit>totCredit orTotCredit>totDebit`
Dim query = _
From product In Bills.AsEnumerable() _
Group product By accode = product.Field(Of String)("ACCODE"), _
BILLNO = product.Field(Of String)("BILLNO") Into g = Group _
Let totDebit = g.Sum(Function(proudct) proudct.Field(Of Decimal)("DEBIT")) _
Let totCredit = g.Sum(Function(proudct) proudct.Field(Of Decimal)("CREDIT")) _
Select New With _
{ _
.ACCODE = accode, _
.billno = BILLNO, _
.TOTALDEBIT = totDebit, _
.TOTALCREDIT = totCredit, _
.BALNACE = totDebit - totCredit _
}
How can this be done?
In the code you h ave shownm you've merely built a query; you haven't executed it yet. And, at any point before you excute it, you may continue to build it:
Dim query = _
From product In Bills.AsEnumerable() _
Group product By accode = product.Field(Of String)("ACCODE"), _
' etc as above....
If onlyCredits Then
query = query.Where(Function(proudct) product.TOTALCREDIT > product.TOTALDEBIT)
Elseif onlyDebits Then
query = query.Where(Function(proudct) product.TOTALCREDIT < product.TOTALDEBIT)
Endif
Note, I'm a C# guy, so please forgive minor syntax errors....
I think you're looking for Dynamic Query Library. Not included in Linq but availble for download from Scott Guthrie's blog:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Both the VB and C# DynamicQuery
samples include a source
implementation of a helper library
that allows you to express LINQ
queries using extension methods that
take string arguments instead of
type-safe language operators.
You can apply strings to filter the exection:
myData.DynamicWhere(string.Format("{0}.Contains(\"{1}\")", filter.field, filter.data.value));

LINQ to SQL Query Syntax

I have several tables that center around an organization table that simply holds a unique ID value. Each Organization can then be at a particular location and have a particular name. The tricky part is that the organizations support location and name changes with a specified effective date of each change. For this example I have 4 relevant tables:
Organization: ID (PK, int, identity)
Location: ID (PK, int, identity), Name (varchar), AltLat (float), AltLong (float)
organization_locations: organization_id(FK, int), location (FK, int), eff_date (datetime)
organization_names: organization_id (FK, int), name (ntext), eff_date (datetime), icon (nvarchar(100))
What I need to retrieve is the list of all locations along with all organizations at a given location as of a specific date and project them into my business entities. In other words, I will have a date provided and need to return for each location, the organization related to the organization_location entry with the most recent eff_date that is less than the date provided. Same thing goes for each organization, I'd need the name as of the date.
Here's what I started with but it doesn't seem to work:
Dim query = From loc In dc.Locations _
Where loc.AltLong IsNot Nothing And loc.AltLat IsNot Nothing _
Select New AnnexA.Entities.AnnexALocation With {.ID = loc.ID, .Name = loc.Location, .Y = loc.AltLat, .X = loc.AltLong, _
.Units = From ol In loc.organization_locations Let o = ol.Organization.organization_names.Where(Function(ed) (ol.eff_date < Date.Parse("1/1/2011"))).OrderByDescending(Function(od) (od.eff_date)).First() Select New AnnexA.Entities.AnnexAMillitaryUnit With {.ID = o.ID, .Name = o.name, .IconPath = o.icon}}
I'd prefer VB syntax but if you can only give me a C# query I can work with that. I've tried a few other variations but I end up getting syntax errors about an expected "}" or members not being a part of an entity set no matter what combination of parenthesis I try.
I think I understand what you're trying to do here, and it looks like some simple joins could get rid of all the complex stuff you're doing at the end of your query. (It's in C# but it should be pretty straight forward to get it to VB)
from loc in dc.Locations
join ol in dc.organization_locations on loc.ID equals ol.location
join orn in dc.organization_names on ol.organization_id equals orn.organization_id
where loc.AltLong != null
&& loc.AltLong != null
&& ol.eff_date < Date.Parse("1/1/2011")
orderby ol.eff_date
select new AnnexA.Entities.AnnexAMillitaryUnit
{ ID = loc.ID, Name = orn.name, IconPath = orn.icon}
Let me know if this works or if it needs any fine tuning...
I need the most recent (as of a given date) location for an organization and name of an organization taken into consideration. Not just all prior to the date. For example, take the U.S. President Elect Obama. In this example there are 3 locations: his house, the Hay-Adams Hotel, and the White House. He also has gone by 3 titles in the past couple months: Senator Obama, President Elect Obama, and soon to be President Obama.
If you were to pass in the date "1/1/2008" into this query, you should get back all 3 locations, with "Senator Obama" (the organization_name) falling within his "house" (the organization_location) and the other two locations should have no "organizations" within them using our sample. If you were to pass in the date of "12/1/2008", he was still technically living at his home but he then had the title of "President Elect Obama" so the results would reflect that. If you were to pass in today's date, his name would still be "President Elect Obama" but his location changed to the "Hay-Adams Hotel". If you pass in any date after "1/20/2009", his title will be "President Obama" and his location will be "The Whitehouse".
I hope that made some sense, even if you have no interest in politics or aren't from the U.S.. I need the results to show me where everything was at and what everything was called at a given point in time.
I ended up getting this working using the following code:
Dim query4 = From loc In dc.Locations _
Let curNames = (From ons In dc.organization_names _
Where ons.eff_date <= ssDate _
Order By ons.eff_date Descending _
Group ons By ons.organization_id Into gNames = Group _
Select New With { _
Key .Key = organization_id, _
.Result = gNames.Take(1)}) _
.SelectMany(Function(a) (a.Result)) _
Let curLocs = (From oLocs In dc.organization_locations _
Where oLocs.eff_date <= ssDate _
Order By oLocs.eff_date Descending _
Group oLocs By oLocs.organization_id Into gLocs = Group _
Select New With { _
Key .Key = organization_id, _
.Result = gLocs.Take(1)}) _
.SelectMany(Function(a) (a.Result)) _
Where loc.AltLat IsNot Nothing And loc.AltLong IsNot Nothing _
Select New AnnexA.Entities.AnnexALocation With { _
.ID = loc.ID, .Name = loc.Location, .Y = loc.AltLat, .X = loc.AltLong, _
.Units = From curLoc In curLocs _
Where curLoc.location = loc.ID _
From curName In curNames _
Where curName.organization_id = curLoc.organization_id _
Select New AnnexA.Entities.AnnexAMillitaryUnit With { _
.ID = curLoc.organization_id, _
.Name = curName.name, _
.IconPath = curName.icon}}
Thanks for taking the time to look at this!