Assignment 3: A LINQ to the Past

And now for the objective I've been building up to since Assignment 1: integration of LINQ queries into our Real Estate Management Application. Which is arguably the most useful tool when it comes to searching for a new home to buy. People want to filter by a dozen different variables, including the distance between available properties and schools, before putting down six figures for their new home... <sidebar> that will never cease to bother me. Life has gotten increasingly more expensive over the last fifty years, and it's become increasingly more difficult/risky to try and afford it. This modern-day indentured servitude makes us more willing to accept sub-par working conditions and reluctant to turn down "mandatory overtime" out of fear of losing our source of income and livelihood. Hail Corporate America, I guess. </sidebar>

This assignment also introduces the Business : Property and School : Property classes.

Business

  • string name
  • readonly BusinessType type — see below for more details
  • readonly string yearEstablished
  • uint activeRecruitment
BusinessType is an enumerated data type we're also introducing into this assignment, with the following values (in this exact order, or your output won't match mine):

Grocery, Bank, Repair, FastFood, DepartmentStore

You will additionally need constructors and various implementations of ToString(), as will be necessary to meet the formatting requirements of the output.

School

  • string name
  • readonly SchoolType — see below for more details
  • string yearEstablished
  • uint enrolled
SchoolType is another enumerated data type we're introducing into this assignment, with the following values (again, in this exact order):

Elementary, HighSchool, CommunityCollege, University

You will additionally need constructors and various implementations of ToString(), as will be necessary to meet the formatting requirements of the output. One thing worth pointing out is that the input files will not define the SchoolType attribute for each School object. Instead, that type is derived by the name of the school itself, e.g., DeKalb High School isn't a Community College but is instead a... High School — why did I need to explain this?

I've also modified the forSale attribute from the input files, such that T is always followed by a : character, and the price (in whole dollars) of the property. This will be obviously necessary when we see the query details below.

With as many individual input files we have now, I've instead uploaded .zip files for both [DeKalb] and [Sycamore]. Since hopefully by this point, we've already established the necessity for partitioning them into separate directories in Assignment 2.

The queries to be implemented are described below:

  1. For Sale Properties Within Price Range
  2. For Sale Residences Within Range of a School
  3. Hiring Business(es) Within Range of a For Sale Property
  4. Specific Residence Parameters
  5. List of Properties Owned by Out-Of-Towners

While there are ways to implement this functionality without using LINQ, it will be a functional requirement for full credit on this assignment that you heavily rely on LINQ to filter your datasets, before ultimately producing the results to a large text box area. For some of these results, there may also be an empty set of results, in which case you would only print a "Your query yielded no matches!" message.

Generally speaking, your button EventHandler methods will follow this structure:

  1. Verify input values (when applicable)
  2. Perform one or more LINQ's
  3. Dump results or print a no-results message
It is perfectly acceptable (and possibly necessary) to do some typecasting within your foreach loops that dump results into the output field. Since ideally, we are always returning either a List or a List of Lists, of Property objects. Which we should then typecast as Businesses or Schools or Houses as necessary, to access those corresponding ToString() methods.
Here's what my Form ended up looking like:



This can help provide some directions on how to implement this, as well as point out features of this that are new from the Form we created in Assignment 2. As with the last assignment, too: you are not required to replicate this design. In fact, I encourage all of you to tackle the design of the Form in whatever way you think makes the most sense to you.

One thing you'll notice is that not a single input field allows for user-provided text input. This is our ideal outcome when it comes to Forms, as these alternative modes of input limit the amount of intentional or accidental mistakes when it comes to input, as well as help protect against malicious Form usage.

Each of the ComboBoxes need to be populated from your source code, with many of their values derived from the input files accordingly. The TrackBars used in Query 1 should iterate from [0, 350000], with two additional requirements: toggling either TrackBar should display the current value of that TrackBar's... Value, in the corresponding Label. Additionally, scrolling the minimum value above the maximum should drag that one along, or conversing with lowering the maximum value below the currently set minimum. This way, you never run into a situation where min > max. It's fine if they are equal to each other, although not particularly useful as a query parameter. The NumericUpDown component for Queries 2 and 3 should iterate from [0, 500], with an increment set to 50; for Query 4, Bed/Bath should iterate from [0, 6], with an increment set to 1; Min. Sq.Ft. should iterate from [1200, 6000], and increment by 250.

The utilization of distance between properties is finally where we'll make use of those (X, Y) coordinate values we've been attaching to every property. For those who need a refresher on how to calculate distance between two Cartestian coordinates, refer to this Google search result. The observant reader might recognize the undefined relationship between properties in DeKalb as compared to Sycamore, since they may both appear in query results. For this assignment, we're going to assume that Sycamore properties are simply offset +250 in the X direction from DeKalb properties. For example, the distance between (40, 40) in DeKalb and (40, 40) in Sycamore would be exactly 250.

Now, while I have no intention of walking you through the code necessary to achieve each of these objectives, some of them are reached through... potentially less than obvious means. So here are some Development Strategies/General Comments.

  1. For Sale Properties Within Price Range: This one is only difficult until you solve the "did the user check residential? and/or business? and/or school?" problem. You do want to divide the output between the two Community objects, which lends this implementation to utilizing the groupby LINQ operator. And by "lends this implementation to", I really mean, "you are required to implement it in this way". Order by price in ascending order.

  2. For Sale Residences Within Range of a School: This is probably the easiest of the bunch, assuming you have the distance problem worked out correctly. Order by distance in descending order.

  3. Hiring Business(es) Within Range of a For Sale Property: This should be relatively easy to put together after the previous two. Although you will need to get creative in order to include the Distance value in the output report. Order by their active recruitment in descending order.

  4. Specific Residence Parameters: This is where I realized I wanted to ramp up the difficulty. Specifically because I am requiring this problem be solved with a single (albeit lengthy) LINQ query. Formatting the header for the output also includes some attention to detail as well, such as avoiding "with 2 bed". As it was with Assignment 2, by selecting "Apartment", we should hide (and subsequently ignore) the garage input fields. Order by price in ascending order.

  5. List of Properties Owned by Out-Of-Towners: once you include an extra method to Communities, this becomes a relatively easy problem to solve.

It might be worth mentioning that I heavily relied on SortedLists in my solutions, since we have unique ID values associated with all properties and people. This does trivialize some of these problems.

Sample Output

While there's only one screenshot demonstrating an error message produced when there are no results, you should absolutely have code in place to accommodate for missing input values or no results in each and every objective to be achieved.

Drop-Down Menus:

Query 1:
Query 2:
Query 3:
Query 4:
Query 5: