Custom Operations and hiding entities in Astoria

I learned a harsh lesson about Visual Studio and saving code snippets in the toolbox yesterday. Knowing that I am not great at coding Entity SQL on the fly, I stuck my code for custom operations in an Astoria Web data service into the VS2008 toolbox. I had two instances of VS open when I did this, so when I closed the instance where I had created the snippets, the instance that was still open is the one that saved the state and they were lost. Note to self: always have a text file with all snippets available when demoing. Oddly, I never used to trust the snippets and only used text files.  I guess I need to get back to a level of cautious (though not rampant) mistrust.

I did surprise myself this morning when I rewrote these from memory and only made one mistake… inserting an “@” where I didn’t need to. I’d call that an A-. It wasn’t a typo, though and it did take a few minute to discover the error which is something I don’t like doing during a conference session, so I just skipped the pain.  (Still only a C+ in my book 🙁 ).

So here, in place of that particular demo, is a post on how to write operations in data services.

The default service provides for all of the capabilities of drilling into, traversing and querying your entities as laid out in this post and in more detail in the Using Astoria document that comes with the downloads and is also on the Astoria site. (Start at to find all resources.)

However you can customize your service by providing explicit query operations and also blocking specific query operations. Don’t forget that you can also build views and sprocs into your model. Views are surfaced as EntitySets so they will be no different than other entities (though read-only) in your web service. Stored Procs are a little different. I have to explore how both of those are currently surfaced in Astoria, but would like to wait until we have bits that are not just prototype.

To create a custom operation you need to use the WebGet attribute. Here’s an operation that finds all customers based on the first name of the contact. (I’m using the AW LT database, and am just lazily querying for something that is a direct property of the customer table.)

<WebGet()> _
Public Shared Function CustbyContact(ByVal aw As AdventureWorksLTEntities, ByVal firstname As String) As ObjectQuery(Of Customer)
  If String.IsNullOrEmpty(firstname) Then
  ‘don’t expect to see this nice error if you are testing
  ‘your services in a browser
    Throw New ArgumentNullException(“firstname”, _
     “You must provide a name to search on”)
  End If

    Return aw.Customer.Where(“it.firstname=@fname”, New ObjectParameter(“fname”, firstname))
  Catch ex As ArgumentException
  ‘this is an opportunity to catch problems with your Entity SQL query when debugging
    Throw ex
  End Try
  End Function

Note that I’m using Entity SQL in this query. While the query


will work if I had used LINQ to Entities for the query, I found that further operators or options such as this example where I additionally sort the response data


do not work unless I have used Entity SQL.

While I have used a WebGet attribute to add additional queries to my service, you can use OnSendEntity and OnReceiveEntity to trap calls to the web service.

This custom operation in my data service prevents any one from querying directly for SalesOrderHeader entities using http://localhost:50000/WebDataService1.svc/SalesOrderHeader.

<OnSendEntity(“SalesOrderHeader”)> _
Public Shared Function RequestOrders(ByVal aw As AdventureWorksLTEntities, ByVal order As SalesOrderHeader) As Boolean
   Return False
End Function

I can still get at SalesOrderHeaders, for example by requesting customers with the expand option for SalesOrderHeader entities.

The using astoria doc details the key components for creating a method that traps requests and also one that traps POST, PUT and DELETE operations.

  Sign up for my newsletter so you don't miss my conference & Pluralsight course announcements!  

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.