Skip to content
Demis Bellot edited this page Oct 25, 2016 · 9 revisions

AutoQuery Service Data Source

The next step after MemorySource in querying for even richer result-sets, whether you want to add custom validation, access multiple dependencies, construct complex queries or other custom business logic, you can use a Service Source instead which lets you call a Service and use its Response as the dynamic Data Source that you can apply Auto Querying logic on.

ServiceSource is very similar to MemorySource however instead of passing in the in-memory collection you want to query directly, you'll need to pass a Request DTO of the Service you want called instead. The response of the Service is then further queried just as if its results were passed into a MemorySource directly.

We'll illustrate with a few examples how to register and use ServiceSources, explore some of their capabilities and provide some examples of when you may want to use them below.

The UserLogin ServiceSource shows you can just pass an empty Request DTO as-is to execute its Service. The RockstarAlbum and GithubRepo Service Sources are however leveraging the built-in Auto Mapping to copy any matching properties from the AutoQuery Request DTO to the downstream GetRockstarAlbums and GetGithubRepos Request DTO's. Finally the responses for the GithubRepo Service is cached for 5 minutes so any subsequent matching requests end up querying the cached result set instead of re-executing the GetGithubRepos Service:

Plugins.Add(new AutoQueryDataFeature { MaxLimit = 100 }
    .AddDataSource(ctx => ctx.ServiceSource<UserLogin>(new GetTodaysUserActivity())),
    .AddDataSource(ctx => ctx.ServiceSource<RockstarAlbum>(ctx.Dto.ConvertTo<GetRockstarAlbums>())),
    .AddDataSource(ctx => ctx.ServiceSource<GithubRepo>(ctx.Dto.ConvertTo<GetGithubRepos>(), 
        HostContext.Cache, TimeSpan.FromMinutes(5)));
);

The implementation of GetTodaysUserActivity Service uses an async OrmLite RDBMS call to get all User Logins within the last day, fetches the Live Activity data from Redis, then merges the disconnected POCO result sets into the UserLogin POCO which it returns:

[Route("/useractivity/today")]
public class QueryTodaysUserActivity : QueryData<User> {}

public async Task<List<UserLogin>> Any(GetTodaysUserActivity request)
{
    var logins = await Db.SelectAsync<UserLogin>(x => x.LastLogin >= DateTime.UtcNow.AddDays(-1));
    var activities = Redis.As<Activity>().GetAll();
    logins.Merge(activities);
    return logins;
}

The GetRockstarAlbums Service shows an example of a calling an existing ad hoc DB Service executing an arbitrary custom Query. It uses the Request DTO Auto-Mapping at the ServiceSource registration to first copy any matching properties from the initial QueryRockstarAlbums Request DTO to populate a new GetRockstarAlbums instance which is what's used to execute the Service with.

In this way the QueryRockstarAlbums AutoQuery Service is essentially decorating the underlying GetRockstarAlbums Service giving it access to AutoQuery features where clients are able to apply further post-querying server logic to an existing Service implementation which now lets them filter, sort, select only a partial list of fields, include additional aggregate queries, etc.

public class QueryRockstarAlbums : QueryData<RockstarAlbum> 
{
    public string Name { get; set; }
    public int[] IdBetween { get; set; }
}

public object Any(GetRockstarAlbums request)
{
    var q = Db.From<RockstarAlbum>();

    if (request.IdBetween != null)
        q.Where(x => x.Id >= request.IdBetween[0] && x.Id <= request.IdBetween[1]);

    if (request.Name != null)
        q.Where(x => x.Name == request.Name);

    return new GetRockstarAlbumsResponse { Results = Db.Select(q) };
}

One thing to notice is that ServiceSource still works whether the results are wrapped in a Response DTO instead of a naked IEnumerable<RockstarAlbum> collection. This is transparently supported as ServiceSource will use the first matching IEnumerable<T> property for Services that don't return a collection.

It should be noted that decorating an existing OrmLite Service is rarely necessary as in most cases you'll be able to get by with just a simple AutoQuery RDBMS query as seen in the Service below which replaces the above 2 Services:

public class QueryRockstarAlbums : QueryDb<RockstarAlbum> {}

The final GetGithubRepos ServiceSource example shows an example of a slightly more complex implementation than a single 3rd Party API call where it adds custom validation logic and call different 3rd Party API Endpoints depending on user input:

public class QueryGithubRepo : QueryData<GithubRepo> 
{
    public string User { get; set; }
    public string Organization { get; set; }
}

public object Get(GetGithubRepos request)
{
    if (request.User == null && request.Organization == null)
        throw new ArgumentNullException("User");

    var url = request.User != null
        ? "https://api.github.com/users/{0}/repos".Fmt(request.User)
        : "https://api.github.com/orgs/{0}/repos".Fmt(request.Organization);

    return url.GetJsonFromUrl(requestFilter:req => req.UserAgent = GetType().Name)
        .FromJson<List<GithubRepo>>();
}

A hidden feature ServiceSources are naturally able to take advantage of due to its behind-the-scenes usage of the new Service Gateway is that the exact code above could still function if the QueryGithubRepo AutoQuery Data Service and underlying GetGithubRepos Service were moved to different hosts :)

Custom AutoQuery Data Implementation

Just like you can Create a Custom implementation in AutoQuery, you can do the same in AutoQuery Data by just defining an implementation for your AutoQuery Data Request DTO. But instead of IAutoQueryDb you'd reference the IAutoQueryData dependency to construct and execute your custom AutoQuery Data query.

When overriding the default implementation of an AutoQuery Data Service you also no longer need to register a Data Source as you can specify the Data Source in-line when calling AutoQuery.CreateQuery().

For our custom AutoQuery Data implementation we'll look at creating a useful Service which reads the daily CSV Request and Error Logs from the new CsvRequestLogger and queries it by wrapping the POCO RequestLogEntry results into a MemoryDataSource:

[Route("/query/requestlogs")]
[Route("/query/requestlogs/{Date}")]
public class QueryRequestLogs : QueryData<RequestLogEntry>
{
    public DateTime? Date { get; set; }
    public bool ViewErrors { get; set; }
}

public class CustomAutoQueryDataServices : Service
{
    public IAutoQueryData AutoQuery { get; set; }

    public object Any(QueryRequestLogs query)
    {
        var date = query.Date.GetValueOrDefault(DateTime.UtcNow);
        var logSuffix = query.ViewErrors ? "-errors" : "";
        var csvLogsFile = VirtualFileSources.GetFile(
            "requestlogs/{0}-{1}/{0}-{1}-{2}{3}.csv".Fmt(
                date.Year.ToString("0000"),
                date.Month.ToString("00"),
                date.Day.ToString("00"),
                logSuffix));

        if (csvLogsFile == null)
            throw HttpError.NotFound("No logs found on " + date.ToShortDateString());

        var logs = csvLogsFile.ReadAllText().FromCsv<List<RequestLogEntry>>();

        var q = AutoQuery.CreateQuery(query, Request,
            db: new MemoryDataSource<RequestLogEntry>(logs, query, Request));

        return AutoQuery.Execute(query, q);
    }
}

This Service now lets you query the Request Logs of any given day, letting you filter, page and sort through the Request Logs of the day. While we're at it, let's also create multiple Custom AutoQuery Data implementations to act as canonical smart links for the above Service:

[Route("/logs/today")]
public class TodayLogs : QueryData<RequestLogEntry> { }
[Route("/logs/today/errors")]
public class TodayErrorLogs : QueryData<RequestLogEntry> { }

[Route("/logs/yesterday")]
public class YesterdayLogs : QueryData<RequestLogEntry> { }
[Route("/logs/yesterday/errors")]
public class YesterdayErrorLogs : QueryData<RequestLogEntry> { }

The implementations of which just delegates to QueryRequestLogs with the selected Date and whether or not to show just the error logs:

public object Any(TodayLogs request) =>
    Any(new QueryRequestLogs { Date = DateTime.UtcNow });

public object Any(TodayErrorLogs request) =>
    Any(new QueryRequestLogs { Date = DateTime.UtcNow, ViewErrors = true });

public object Any(YesterdayLogs request) =>
    Any(new QueryRequestLogs { Date = DateTime.UtcNow.AddDays(-1) });

public object Any(YesterdayErrorLogs request) =>
    Any(new QueryRequestLogs { Date = DateTime.UtcNow.AddDays(-1), ViewErrors = true });

View Request Logs in AutoQuery Viewer

And with no more effort we can jump back to /ss_admin/ and use AutoQuery Viewer's nice UI to quickly inspect Todays and Yesterdays Request and Error Logs :)



  1. Getting Started

    1. Creating your first project
    2. Create Service from scratch
    3. Your first webservice explained
    4. Example Projects Overview
    5. Learning Resources
  2. Designing APIs

    1. ServiceStack API Design
    2. Designing a REST-ful service with ServiceStack
    3. Simple Customer REST Example
    4. How to design a Message-Based API
    5. Software complexity and role of DTOs
  3. Reference

    1. Order of Operations
    2. The IoC container
    3. Configuration and AppSettings
    4. Metadata page
    5. Rest, SOAP & default endpoints
    6. SOAP support
    7. Routing
    8. Service return types
    9. Customize HTTP Responses
    10. Customize JSON Responses
    11. Plugins
    12. Validation
    13. Error Handling
    14. Security
    15. Debugging
    16. JavaScript Client Library (ss-utils.js)
  4. Clients

    1. Overview
    2. C#/.NET client
      1. .NET Core Clients
    3. Add ServiceStack Reference
      1. C# Add Reference
      2. F# Add Reference
      3. VB.NET Add Reference
      4. Swift Add Reference
      5. Java Add Reference
    4. Silverlight client
    5. JavaScript client
      1. Add TypeScript Reference
    6. Dart Client
    7. MQ Clients
  5. Formats

    1. Overview
    2. JSON/JSV and XML
    3. HTML5 Report Format
    4. CSV Format
    5. MessagePack Format
    6. ProtoBuf Format
  6. View Engines 4. Razor & Markdown Razor

    1. Markdown Razor
  7. Hosts

    1. IIS
    2. Self-hosting
    3. Messaging
    4. Mono
  8. Security

    1. Authentication
    2. Sessions
    3. Restricting Services
    4. Encrypted Messaging
  9. Advanced

    1. Configuration options
    2. Access HTTP specific features in services
    3. Logging
    4. Serialization/deserialization
    5. Request/response filters
    6. Filter attributes
    7. Concurrency Model
    8. Built-in profiling
    9. Form Hijacking Prevention
    10. Auto-Mapping
    11. HTTP Utils
    12. Dump Utils
    13. Virtual File System
    14. Config API
    15. Physical Project Structure
    16. Modularizing Services
    17. MVC Integration
    18. ServiceStack Integration
    19. Embedded Native Desktop Apps
    20. Auto Batched Requests
    21. Versioning
    22. Multitenancy
  10. Caching

  11. Caching Providers

  12. HTTP Caching 1. CacheResponse Attribute 2. Cache Aware Clients

  13. Auto Query

  14. Overview

  15. Why Not OData

  16. AutoQuery RDBMS

  17. AutoQuery Data 1. AutoQuery Memory 2. AutoQuery Service 3. AutoQuery DynamoDB

  18. Server Events

    1. Overview
    2. JavaScript Client
    3. C# Server Events Client
    4. Redis Server Events
  19. Service Gateway

    1. Overview
    2. Service Discovery
  20. Encrypted Messaging

    1. Overview
    2. Encrypted Client
  21. Plugins

    1. Auto Query
    2. Server Sent Events
    3. Swagger API
    4. Postman
    5. Request logger
    6. Sitemaps
    7. Cancellable Requests
    8. CorsFeature
  22. Tests

    1. Testing
    2. HowTo write unit/integration tests
  23. ServiceStackVS

    1. Install ServiceStackVS
    2. Add ServiceStack Reference
    3. TypeScript React Template
    4. React, Redux Chat App
    5. AngularJS App Template
    6. React Desktop Apps
  24. Other Languages

    1. FSharp
      1. Add ServiceStack Reference
    2. VB.NET
      1. Add ServiceStack Reference
    3. Swift
    4. Swift Add Reference
    5. Java
      1. Add ServiceStack Reference
      2. Android Studio & IntelliJ
      3. Eclipse
  25. Amazon Web Services

  26. ServiceStack.Aws

  27. PocoDynamo

  28. AWS Live Demos

  29. Getting Started with AWS

  30. Deployment

    1. Deploy Multiple Sites to single AWS Instance
      1. Simple Deployments to AWS with WebDeploy
    2. Advanced Deployments with OctopusDeploy
  31. Install 3rd Party Products

    1. Redis on Windows
    2. RabbitMQ on Windows
  32. Use Cases

    1. Single Page Apps
    2. HTML, CSS and JS Minifiers
    3. Azure
    4. Connecting to Azure Redis via SSL
    5. Logging
    6. Bundling and Minification
    7. NHibernate
  33. Performance

    1. Real world performance
  34. Other Products

    1. ServiceStack.Redis
    2. ServiceStack.OrmLite
    3. ServiceStack.Text
  35. Future

    1. Roadmap
Clone this wiki locally