Reducing magic and increasing familiarity by obsoleting QueryResultRows
November 1, 2017 3:15 pm Leave your thoughtsTo make the Db.SQL
API more familiar and easier to use, we have chosen to obsolete QueryResultRows
and instead use IEnumerable
. This means that the compiler will give warnings when using code that relies on QueryResultRows
. The obsoletion warnings have been added to version 2.3.1.7660 and 2.4.
QueryResultRows
is the result type of Db.SQL
, as described on the page Querying with SQL in the docs.
The change of removing QueryResultRows
will be made later in version 2.4. Thus, these changes will not break any code on version 2.3.1. If you are running version 2.4, fix the warnings as soon as possible to avoid breaking code when QueryResultRows
is removed.
Motivation
There are four primary reasons to move from QueryResultRows
to IEnumerable
:
QueryResultRows
does not have any extra features compared toIEnumerable
but requires extra documentation and support since it’s different from the familiarIEnumerable
interface.QueryResultRows
has some implicit converters that are hard to understand, fragile and are rarely used.- The property
First
ofQueryResultRows
does the same as Linq’sFirstOrDefault()
but without the familiarity ofFirstOrDefault()
. - By returning
IEnumerable
fromDb.SQL
, it conforms better with Linq since Linq expectsIEnumerable
.
Changes
Obsolete QueryResultRows
The main change is that QueryResultRows
is obsoleted. This will generate warnings when QueryResultRows
is used:
// 'QueryResultRows<Person>' is obsolete: 'Use IEnumerable<T> interface instead.'
public QueryResultRows<Person> Persons => Db.SQL<Person>(...);
// 'QueryResultRows<Person>' is obsolete: 'Use IEnumerable<T> interface instead.'
QueryResultRows<Person> persons = Db.SQL<Person>(...);
foreach (var person in persons) { }
As the warning says, these are fixed by changing QueryResultRows
to IEnumerable
:
public IEnumerable<Person> Persons => Db.SQL<Person>(...);
IEnumerable<Person> persons = Db.SQL<Person>(...);
foreach (var person in persons) { }
No warnings are generated if you enumerate the result without specifying the type, or when calling FirstOrDefault()
:
var persons = Db.SQL<Person>(...);
foreach (var person in persons) { } // No warning
var person = persons.FirstOrDefault(); // No warning
These will still work the same way when QueryResultRows
is removed.
Obsolete NewQueryResultRows
NewQueryResultRows
is the lesser-known sibling of QueryResultRows
. It can be updated the same way as QueryResultRows
– by changing it to IEnumerable
.
Obsolete Rows
Rows
is the abstract base class for QueryResultRows
and NewQueryResultRows
. It can be directly replaced with IEnumerable
without any problems
Obsolete First
property
The First
property was used to retrieve the first entry in a QueryResultRows
, Rows
, or NewQueryResultRows
. Using First
will now generate a warning:
// QueryResultRows<object>.First' is obsolete: 'Use linq extension FirstOrDefault() instead.
var person = Db.SQL<Person>(...).First;
Importing System.Linq
and using FirstOrDefault()
instead of First
will remove the warning and it will still work the same except for when the return type is a numeric type and the query returns null
, then FirstOrDefault()
will throw RuntimeBinderException
instead of returning 0
as First
did. This can be fixed by using nullable types.
Obsolete implicit converter from QueryResultRows
to Response
The implicit converter from QueryResultRows
to Response
is obsoleted together with the IQueryRowsResponse
. The implicit converter and IQueryRowsResponse
will be removed in 2.4. If you rely on this implicit conversion, use an explicit conversion instead.
Obsolete implicit converter from Rows
to Typed JSON array
The implicit converter from Rows
to Typed JSON arrays Arr<T>
is obsoleted. The replacement is to set the Data
property or use bindings in the code-behind.
For example, setting Rows
to an Arr<T>
will now generate a warning:
{
"Friends": [ { } ]
}
// 'Arr<PersonPage>.implicit operator Arr<PersonPage>(Rows)' is obsolete: The implicit converter will be removed in a later version. Set Data property instead or use binding.
var personPage = new PersonPage();
personPage.Friends = Db.SQL<Person>(...);
To remove the warning but maintain the functionality, set the Data
property or use code-behind bindings:
var personPage = new PersonPage();
personPage.Friends.Data = Db.SQL<Person>(...);
partial class PersonPage : Json
{
public IEnumerable<Person> Friends => Db.SQL<Person>(...);
}
These bindings are described in more detail on the data bindings page in the docs.
Summary
We hope that these changes make the APIs easier to use. It removes some of the magic that was happening behind the scenes with implicit conversions and brings us closer to familiar APIs such as IEnumerable
.
If you have any questions on how to adapt your code to these changes, create an issue in the Starcounter/Home repository and we will help you out.
Categorised in: Development
This post was written by Erlend Landrö