ChannelFactory<T>
has a virtual method CreateChannel()
. If this is not overridden, it uses dynamic code generation, which fails on MonoTouch.
The solution is to override it and provide your own compile-time implementation.
Below is an old service implementation of mine that at least used to work on MonoTouch. I split it up into 2 partial classes - the first one being linked in all builds, the 2nd only in the iOS builds (allowing the dynamic generation mechanism to still work on windows).
I've stripped it down to only contain 1 service call.
TransactionService.cs:
public partial class TransactionService : ClientBase<IConsumerService>, IConsumerService
{
public TransactionService()
{
}
public TransactionService(string endpointConfigurationName) :
base(endpointConfigurationName)
{
}
public TransactionService(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public TransactionService(string endpointConfigurationName, EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public TransactionService(Binding binding, EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}
public AccountBalanceResponse GetAccountBalance( AccountBalanceQuery query )
{
return Channel.GetAccountBalance( query );
}
}
TransactionService.iOS.cs:
ConsumerServiceClientChannel
which executes the calls via reflection)
public partial class TransactionService
{
protected override IConsumerService CreateChannel()
{
return new ConsumerServiceClientChannel(this);
}
private class ConsumerServiceClientChannel : ChannelBase<IConsumerService>, IConsumerService
{
public ConsumerServiceClientChannel(System.ServiceModel.ClientBase<IConsumerService> client) :
base(client)
{
}
// Sync version
public AccountBalanceResponse GetAccountBalance(AccountBalanceQuery query)
{
object[] _args = new object[1];
_args[0] = query;
return (AccountBalanceResponse)base.Invoke("GetAccountBalance", _args);
}
// Async version
public IAsyncResult BeginGetAccountBalance(AccountBalanceQuery query, AsyncCallback callback, object asyncState )
{
object[] _args = new object[1];
_args[0] = query;
return (IAsyncResult)base.BeginInvoke("GetAccountBalance", _args, callback, asyncState );
}
public AccountBalanceResponse EndGetAccountBalance(IAsyncResult asyncResult)
{
object[] _args = new object[0];
return (AccountBalanceResponse)base.EndInvoke("GetAccountBalance", _args, asyncResult);
}
}
}
EDIT: I just tested this with the latest MT (5.2) - it no longer needs all that extra boiler plate I had in there before, just the CreateChannel() override. I've cleaned up the sample code to match.
EDIT2: I added an async method implementation.
Invoke()
method is only available for some target frameworks and is not part of netstandard2.0. We are left with a Begin/EndInvoke() that only throws Null ref ex. – StingyJackBeginInvoke()
andEndInvoke()
inside theChannelBase<T>
and accomplish the same as theInvoke()
, but its not working for me. I get null reference exceptions, I think are because the runtime is not able to match the Begin invocation to the correct End invocation. I tried a few different ways, and still no joy =< – StingyJack