13

monotouch はネイティブ コードにコンパイルされるため、動的呼び出しが許可されないなどの制限があります。

しかし、.net にはたくさんのクラスがあり、ChannelFactory ダイナミックを使用して wcf サービスを呼び出します。モノタッチでは、slsvcutil を使用して wcf プロキシ クラスを生成する必要がありますが、slsvcutil は多くの不要な追加コード (巨大) を生成し、ClientBase クラスを介した WCF インフラストラクチャとの結合が高いため、コンシューマーの単体テストを困難にします。

ChannelFactory 以外のより良い解決策はありますか? ChannelFactory などのサービスの呼び出し方法をより詳細に制御できるように、コードを手動で記述したいと考えています。

==========

        ChannelFactory<IMyContract> factory = new ChannelFactory<IMyContract>(binding, endpointAddress);
        return factory.CreateChannel();   

//==> 例外をスローします: MonoTouch は動的プロキシ コード生成をサポートしていません。このメソッドまたはその呼び出し元をオーバーライドして、特定のクライアント プロキシ インスタンスを返す

4

2 に答える 2

20

ChannelFactory<T>には仮想メソッドがありCreateChannel()ます。これがオーバーライドされていない場合、MonoTouch で失敗する動的コード生成が使用されます。

解決策は、それをオーバーライドして、独自のコンパイル時の実装を提供することです。

以下は、少なくとも MonoTouch で動作していた私の古いサービスの実装です。私はそれを 2 つの部分クラスに分割しました。最初のクラスはすべてのビルドでリンクされ、2 番目は iOS ビルドでのみリンクされます (動的生成メカニズムが Windows で引き続き機能するようにします)。
1 つのサービス コールのみを含むように、それを削除しました。

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リフレクションを介して呼び出しを実行します)

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);
        }

    }
}

編集: これを最新の MT (5.2) でテストしたところです。以前にあった余分なボイラー プレートはすべて不要で、CreateChannel() オーバーライドだけが必要です。一致するようにサンプル コードをクリーンアップしました。

EDIT2:非同期メソッドの実装を追加しました。

于 2012-04-07T16:56:01.100 に答える
1

ここで用語を混乱させている可能性があると思います- ChannelFactoryジェネリック型であり、dynamicではありません。

MonoTouch のドキュメントによると、 MonoTouchの Generics サポートには制限がありますが、ここでは ChannelFactoryで問題ありません。

ChannelFactory を使ってみましたか?

于 2012-04-07T13:19:07.117 に答える