Reducing magic and increasing familiarity by obsoleting QueryResultRows

November 1, 2017 3:15 pm Published by Leave your thoughts

To 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 to IEnumerable but requires extra documentation and support since it’s different from the familiar IEnumerable interface.
  • QueryResultRows has some implicit converters that are hard to understand, fragile and are rarely used.
  • The property First of QueryResultRows does the same as Linq’s FirstOrDefault() but without the familiarity of FirstOrDefault().
  • By returning IEnumerable from Db.SQL, it conforms better with Linq since Linq expects IEnumerable.

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:

This post was written by Erlend Landrö

Leave a Reply

Your email address will not be published. Required fields are marked *