Welcome

LINQ to OWIN is middleware that allows you to code your Katana/OWIN web applications as a set of reactive queries using Rx (Reactive Extensions) for .NET.

Huh? Please explain...

Owin is basically a slim interface for defining ASP.NET applications with an opt-in feature model. The idea is to provide a barebones web host with an interface that allows users to opt-in to only the middleware that they need, rather than the standard ASP.NET opt-out feature model. Katana provides middleware exposing common ASP.NET features from which you can choose.

LINQ is a technology that allows you to write declarative queries in .NET, similar to SQL. However, unlike SQL, LINQ is a general-purpose query pattern. LINQ enables you to query sequences of objects in memory (LINQ to Objects), SQL databases (Entity Framework), asynchronous events (IObservable<T>), among other data sources.

Rx (Reactive Extensions) provides LINQ operators for IObservable<T>, allowing you to easily compose queries for heterogeneous, asynchronous data sources.

Page == Async Request Query
We typically think of web applications as a set of pages, each page responding concurrently to requests for a particular virtual path. But flip it around, and requests are just another type of asynchronous data source that we can query with Rx! Our pages become sets of asynchronous queries rather than objects. The queries together form a declarative processing pipeline.

Async Request Query -> Page
Each query works as follows:
  1. A query receives a request.
  2. The query may choose to handle the request, or not, based on the HTTP method and virtual path specified.
  3. The query processes the request, sometimes causing side effects like persisting state in a database.
  4. Finally, the query projects a response, typically in the form of an encoded string (HTML or XML), which the LINQ to OWIN infrastructure automatically writes to the response stream.

Examples

The following code snippet shows a reactive query for POST requests to a page located at the /response path. Notice that the query loads the form data asynchronously in a single line of code, without introducing unnecessary concurrency.

yield return from context in host["/response"].PostRequests
             from form in context.Request.ReadFormAsync()
             let number = form.TryParseDecimal("number")
             select number.HasValue
                  ? @"<div>You entered: <strong>" + number + @"</strong></div>
                      <div>" + Html.Link(
                                 "/hello?number=" + number, 
                                 "Print &quot;Hello Linq to Owin&quot; " 
                                   + number + " time(s)") + @" 
                      </div>"
                  : @"<div>" + Html.Link("/", "Please enter a number!") + "</div>";

This is just a very simple example of what's possible. Rx provides many LINQ operators for composing asynchronous queries, such as filtering, projecting, combining, joining and grouping, among others. Many of the operators support Task<T> as well as IObservable<T>.

For example, creating a mashup page for authenticated users is easy, even when the data comes from multiple sources, such as third-party web servers, the local file system, and your own database. You'd probably want to send off multiple requests concurrently to get data from the web service, disk and database simultaneously. And while that's going on, you don't want to block the thread; instead, you want to ensure that your application remains responsive to other requests. The Zip operator is perfect for this scenario. Your query might look something like this:

yield return from context in host["/mashup"].GetRequests
             where context.Authentication.User.Identity.IsAuthenticated
             from data in thirdPartyClientProxy.GetWidgets().Zip(
               ReadFromFile(),  // IObservable<string>
               LoadFromDatabase(),  // IObservable<DatabaseRecord>
               (widgets, file, records) => new { widgets, file, records })
             select GetHtmlForMashup(data.widgets, data.file, data.records);

Getting Started

See the Documentation for details.

Last edited May 11 at 4:58 AM by davedev, version 16