I think you are correct. The basic idea looks something like:
/// <summary>
/// Invokation helper for Actions in the ReSTful world, this is provided since currently no methods exist to
/// do this in the Microsoft Services framework that I could find.
/// </summary>
/// <typeparam name="InType">The type of the entity to invoke the method against</typeparam>
public class InvokationHelper<InType, OutType>
{
/// <summary>
/// Invokes the action on the entity passed in.
/// </summary>
/// <param name="container">The service container</param>
/// <param name="entity">The entity to act upon</param>
/// <param name="ActionName">The name of the action to invoke</param>
/// <returns>OutType object from the action invokation</returns>
public OutType InvokeAction(Container container, InType entity, string ActionName)
{
string UriBase = container.GetEntityDescriptor(entity).SelfLink.AbsoluteUri;
string UriInvokeAction = UriBase + "/" + ActionName;
Debug.WriteLine("InvokationHelper<{0}>.InvokeAction: {1}", typeof(InType), UriInvokeAction);
try
{
IEnumerable<OutType> response;
response = container.Execute<OutType>(
new Uri(UriInvokeAction),
"POST",
true
);
return response.First();
}
catch (Exception e)
{
throw e;
}
}
/// <summary>
/// Invokes the action on the entity passed in.
/// </summary>
/// <param name="container">The service container</param>
/// <param name="entity">The entity to act upon</param>
/// <param name="ActionName">The name of the action to invoke</param>
/// <returns>An enumeration of OutType object from the action invokation</returns>
public IEnumerable<OutType> InvokeActionEnumerable(Container container, InType entity, string ActionName)
{
string UriBase = container.GetEntityDescriptor(entity).SelfLink.AbsoluteUri;
string UriInvokeAction = UriBase + "/" + ActionName;
Debug.WriteLine("InvokationHelper<{0}>.InvokeAction: {1}", typeof(InType), UriInvokeAction);
try
{
IEnumerable<OutType> response;
response = container.Execute<OutType>(
new Uri(UriInvokeAction),
"POST",
true
);
return response;
}
catch (Exception e)
{
throw e;
}
}
}
}
I'm sure there are many more elegant ways to do this. If you've written any code around this I'd love to see it. My C# around the edges of the language (like creating generic ways to invoke methods on types that aren't defined until runtime and such) are not the strongest.
Invokation is like:
InvokationHelper<MyObjectType, MyObjectType> helper = new InvokationHelper<MyObjectType, MyObjectType>();
try
{
MyObjectType resultObject = helper.InvokeAction(container, myServiceObject, "MyActionName");
}
catch (Exception e)
{
// Handle failure of the invokation
}
It should be noted that in order to get extended types, you need to decorate your EntitySetControllers Action methods with the [FromODataUri] attribute. This will enforce appropriate Edm typing on passed key parameters. Without this, you will get parse errors on from Edm for types which are decorated in the URI line. For example, a EntitySet with a URI like .../EntitySet(12345L)/ActionName will throw errors in parse. Nominally, the intent is to decode the parameters to a type of Edm.Int64, but this does not occur without the [FromODataUri] attribute as:
[HttpPost]
public ReturnEntityType ActionName([FromODataUri]long key)
{
...
}
This was a very frustrating bug for me to hunt, and I had filed it with MS as a bug. Turns out it just needed type decoration on the input parameter.