This page probably does not do much for you
The query manager has been with re-motion from its very beginning, and it works very well. However, the recent introduction of re-linq technology into re-motion (see re-linq made the query manager obsolete for almost all queries. For some rare cases, re-linq (and Linq in general) has its limits, and you might wish to get closer to the SQL-metal in your project. Use the query manager when
- re-linq generates slow queries
- you want to use database features not covered by re-linq (rare)
- your queries can't be expressed in (re-) Linq, because they are too complex
In other words, this paper on the query manager won't be interesting for most re-motion programmers. This paper is a fringe-programme for experts.
In the following sections we will re-examine the
FindPersonsByLocation methods from the re-store chapter in the PhoneBook tutorial (FIXME). The author assumes that the reader is familiar with re-store already. Note that these simple methods are for illustration only. In practice it is unnecessary work to use the query manager for queries as simple as
FindPersonsByLocation, because for those, re-linq will work just fine.
Queries intended for the query manager are stored in an XML "configuration" file, typically named
queries.xml – a regular staple when programming databases on Microsoft Windows. The
queries.xml file gives SQL queries an ID for identifying them from program code, and specifies the query's parameter(s), if any. Of course you can name the query manager configuration file any way you want, but "queries.xml" is the canonical name. You find a complete
queries.xml for the exercises here at FIXME.
In the PhoneBook tutorial, the following static method for retrieving all Location objects from the database is introduced. The method is based on Linq, of course:
The fun and easy way, the modern way! As old-schoolers at rubicon remember, the query man way of implementing this simple method is slightly more cumbersome. And less modern.
It requires some preparatory work. You declare the actual SQL statement in the
queries.xml file and place it where both your library and your client application can find it at run-time. Here is the
queries.xml listing for the query manager version of
You can copy this basic
queries.xml file from the location
Of course there is an API for constructing query objects entirely with C# code, but this is usually more work.
Understanding query nodes
The most important item is the node
<query id="GetLocations" type="collection">.
This snippet of uncommented code requires some discussion, so there you go with a diagram and a set of keys to it:
- The id
QueryManGetLocationssimply is a handle for your code to identify the query. By convention (and good practice), this id should have the same name as the method that uses the query.
- The type "
collection" signals that the query returns more than one object, potentially none at all (the alternative is "
LocationViewis what programmers can use for actually accessing object instances for reading. As the name suggests, LocationView is a... wait for it... database view. So if you write a query that only reads data, use "X-View" instead "X-", where "X-" is the domain object class. (Views abstract away the particular type of table inheritance used.)
Declaring the query in
queries.xmlis not enough, of course. You must wrap it up in a method as well. For the given query you do that by
- creating a new query object from that <query> node in queries.xml
- passing that query object to the query manager
- asking the query manager to execute the query and give you the instantiated domain object(s)
Just as with Linq, you let re-store's query factory create the query for you and let that query retrieve the data. In contrast to the Linq-query, the use of the current transaction for retrieved data is not invisible. When using the query manager, you explicitly work with
ClientTransaction.Current. You ask the current transaction itself to load the desired data.
Here is the listing for the
QueryManGetLocations method for the Locations class, wrapping the query:
This won't compile, because the
Query type is not known to
Location.cs yet. Supplement
to fix this.
As you can see in the listing, we pass the string
QueryManGetLocations as an ID and as a parameter to the
Query constructor, so that it can locate the node with the query in
queries.xml (Point 1. in the annotated illustration above). What should also be appearant from this listing is that the query manager is part of the current client transaction. We ask you to defer deeper understanding to later and treat this as a recipe for the time being.
Please note that this code does NOT establish a client transaction. Just like its Linq-programmed relative,
QueryManGetLocations does assume that there is a valid client transaction (
ClientTransaction.Current...), but setting up this client transaction is the responsibility of the user of this function.
In order to make this code work for your application, add the static
QueryManGetLocations method above to your
Location class and add a
queries.xml file to your PhoneBook.Domain project. Then type (or copy) the code from the sample
queries.xml listed above into that new queries.xml file in your project.
However, you must make it known to your
PhoneBook.Sample sub-project, too. Here is a brief how-to:
- Right-click your
PhoneBook.Sampleproject and go along Add->Existing item...
- The file picker dialog opens, but it doesn't show you any XML files, because the filter is set to
.cs, etc. Use the drop down list to select
- Select the
queries.xmlfile from the
PhoneBook.Domainpeer directory, but DON'T click Add.
- Instead, click on the small arrow on the Add-button:
- Select Add as link.
queries.xmlshows up in the
- Right-click this new link that looks like the real queries.xml thing and select "properties".
- The Copy to Output Directory property contains a drop list. Select copy always or copy if newer. You are done.
At this point, you are ready to try out your query. Do the obvious thing and code your
Main() like this:
As you can see, we must set up the client transaction for
Location.QueryManGetLocations () does not bring its own.
FindPersonsByLocation revisited – query methods with parameters
Location.QueryManGetLocations () does not require a parameter, but what about the query-wrapper to get to all the people living at a given location? This is how you invoke the query-wrapper:
The query behind the
GetPersons-method is easy to do in the database, just use
If your database still contains the emperor and the empress and their palace location, you can easily try this out yourself. Run
LIKE '%brunner%' if umlauts scare you.)
This will give you a row for the "Schönbrunner Schloßstraße" location. Its first column is an ID with the GUID. Copy that GUID, and use it in the SELECT for finding the royal couple:
This should give you two rows for the emperor and the empress. (Note that
dbschema.exe has rewritten the
LocationID in the database. This is the default behavior of
dbschema.exe: if a property is a reference to another domain object, the name is modified in this fashion.)
The Location's GUID is what must be passed to query in a wrapper like
The most interesting part in this listing is probably the call to
because this is the spot where the
Location object's ID is actually passed to the query.
After adding both the
QueryManFindPersonsByLocation XML snippet to
queries.xml and the
QueryManFindPersonsByLocation C# snippet to
Location, you are ready to try out this improvement.
The following updated version of the
Main() method lists all locations, persons and phone-numbers:
The code presented here is part of the PhoneBook sample. The corresponding methods are parked in partial extensions to the
Program class, respectively: