The MethodLinq concept

There are now a lot of Linq providers around that do various different things. However, some interfaces were never meant to have linq. In fact there are particular APIs out there that we struggle to use without even thinking about adding linq.

The idea here is that what if there was a linq provider that was able to build and pass parameters into complex methods and interfaces. Why on Earth would you want to do this? Well, some interfaces, for example web services can end up with generate deeply nested properties, its nearly always a outright pain to build the required properties and objects to pass in. Another example would be a method that takes in a large number of complex types, in this case we might only be interested in setting a few properties on a few objects. And yes, you’re correct when you say ‘that’s what overloads are for’, so just hear me out and then judge for yourself.

Some examples

The method definition:

public List<buildingroomcategories> GetRates(Contact orderer, Contact travelAgent, Contact travelPoint, enumResChannel priceOwner, List<buildingroomcategories> buildings, enumRateRequestScope scope, int allotmentNumber, enumGuestRateType guestRateType, params RateRequestParameters[] rateRequestList) 

So as you can imagine, there is a lot of data and objects that need to be flying around in order to access this method.

Access via MethodLinq:

var rateQry = from rate in agent.Query().Rates where
                  rate.Building.ID == buildingId &&
                  rate.Parameters.FromDate == today &&
                  rate.Parameters.ToDate == today.AddDays(3) &&
                  rate.Parameters.PackageCode == "BAR"
              select rate;
var rateResponse = rateQry.Single();

Starting to get the idea? Don’t worry about the confusing method signature, just use the defined queryable properties to add what makes sense. Provided your destination method has a ‘design by contract’ implementation or even some basic null checking your in business.

Lets try another example, take the Paypal webservice. Method Definition:

SetExpressCheckoutResponse SetExpressCheckout(SetExpressCheckoutRequest request);

It looks easy enough, but the ‘SetExpressCheckoutRequest’ object turns out to be deeply nested. Using the .net3.5 constructors, this is what it takes to setup a request object:

var normalResponse = service.SetExpressCheckout(new PaypalAPI.SetExpressCheckoutRequest 
{ 
    RequesterCredentials = new PaypalAPI.CustomSecurityHeaderType 
    { 
        Credentials = new PaypalAPI.UserIdPasswordType 
        { 
            Username = "myaccount", 
            Password = "mypassword", 
            Signature = "signature" 
        } 
    }, 
    SetExpressCheckoutReq = new PaypalAPI.SetExpressCheckoutReq 
    { 
        SetExpressCheckoutRequest = new PaypalAPI.SetExpressCheckoutRequestType 
        { 
            SetExpressCheckoutRequestDetails = new PaypalAPI.SetExpressCheckoutRequestDetailsType 
            { 
                CallbackURL = "http://www.blah.com" 
            } 
        } 
    } 
});

And here is the same thing with MethodLinq:

var query = from item in service.Query().SetExpressCheckout 
            where 
            item.Request.RequesterCredentials.Credentials.Username == "myaccount" && 
            item.Request.RequesterCredentials.Credentials.Password == "mypassword" && 
            item.Request.RequesterCredentials.Credentials.Signature == "signature" && 
            item.Request.SetExpressCheckoutReq.SetExpressCheckoutRequest.SetExpressCheckoutRequestDetails.CallbackURL == "http://www.blah.com" 
            select item;

It does feel a little better, obviously at this stage its not perfect, but it does allow me to worry less about the crazy plethora of objects required to set the properties I want.

How its done.

Currently, it is possible to create MethodLinq access over a class’s methods with very few lines of code. So potentially, ANYONE can have a basic linq provider over their api in no time flat.

As an example, here is the mapping for the SetExpressCheckout method on the Paypal API. First it is necessary to create the following mapping class:

[QueryableMethodAttribute(MethodName = "SetExpressCheckout", Types = new Type[] { typeof(SetExpressCheckoutRequest) })]
public class LinqSetExpressCheckout : MethodQueryData<SetExpressCheckoutResponse>
{
    [QueryableMethodParameterAttribute(ParameterName = "request")]
    public SetExpressCheckoutRequest Request { get; set; }
}

This basically defines the method that is to be called, including the overloads (so we can find it via reflection), then the properties specified with ‘MethodParameterAttribute’ are those that you will use to query the method.

public class PaypalContext : MethodContext<PayPalAPIAAInterface>
{
    public PaypalContext(PayPalAPIAAInterface webservice)
        : base(webservice)
    {
        
    }

    public MethodLinqQuery<linqsetexpresscheckout> SetExpressCheckout { get { return new MethodLinqQuery<LinqSetExpressCheckout>(new MethodQueryProvider(base.OwnerClass)); } }
}

public static class PaypalContextExtensions
{
    public static PaypalContext Query(this PayPalAPIAAInterface service)
    {
        return new PaypalContext(service);
    }
}

From these few snippets of meta information the provider will then parse over the properties you’ve used and build the parameter objects for you, when this is done it puts the parameters in the correct order and finally invokes the desired function. It is also currently possible to have an anonymous expression on the ‘select’ if only a subset of the result is required.

Sourcecode

The sourcecode for this project can be viewed in the System.BusinessObjects.MethodLinq repository. Most of the existing unit tests I have for this project are based on a non-public api, it will probably take some additional time to write these against a mock interface.

As always, if you have any thoughts leave them below, hopefully this library will one day be able to unfuddle APIs and methods that require more work then they should.

.NET Code Snippets General
Posted by: Brendan Kowitz
Last revised: 21 Sep 2013 12:15PM

Comments

No comments yet. Be the first!

No new comments are allowed on this post.