別の方法は、サービスコントラクトのバージョン管理を実装することです。その時点で、このバージョン管理戦略ページにリストされているように、WCF独自の機能を利用して、クライアントを壊さない小さな変更を無視できます。
図1では、操作シグニチャに新しいパラメータを追加するとき、操作シグニチャからパラメータを削除するとき、および新しい操作を追加するときに、クライアントが影響を受けないことがわかります。
まだ重大な変更がある場合、またはクライアントが両方のバージョンをサポートする必要がある場合(デプロイ戦略がわからないため、間違っている場合は修正してください)、異なるエンドポイントで異なるバージョンのサービスを提供し、WCFを使用できます。クライアントコードのクライアントファクトリ。これは、適切なエンドポイントのクライアントを返すように構成できます。
この時点で、さまざまなクライアントのさまざまな実装を分離しました。これは、おそらく現在よりもクリーンで、メンテナンスの悪夢が少なくなっています。
物事を明確にするための非常に基本的なサンプル実装:古いものと新しいものの2つの異なるサービス契約があるとします。
[ServiceContract(Name = "Service", Namespace = "http://stackoverflow.com/2012/03")]
public interface IServiceOld
{
[OperationContract]
void DoWork();
}
[ServiceContract(Name = "Service", Namespace = "http://stackoverflow.com/2012/04")]
public interface IServiceNew
{
[OperationContract]
void DoWork();
[OperationContract]
void DoAdditionalWork();
}
両方のサービスの名前は同じですが、名前空間が異なることに注意してください。
拡張サービスと新しいサービス、および古いサービスの両方をサポートできる必要があるクライアントがあるという問題に対処しましょう。以前にDoWorkを呼び出したときにDoAdditionalWorkメソッドを呼び出し、クライアント側の状況を処理したいとします。これは、仮想的にDoAdditionalWorkがクライアントからの追加の引数を必要とする可能性があるためです。その場合、サービスの構成は次のようになります。
<service name="ConsoleApplication1.Service">
<endpoint address="http://localhost:8732/test/new" binding="wsHttpBinding" contract="ConsoleApplication1.IServiceNew" />
<endpoint address="http://localhost:8732/test/old" binding="wsHttpBinding" contract="ConsoleApplication1.IServiceOld" />
...
</service>
これで、サービス側ができました。興味深い部分です。同じインターフェイスを使用してサービスと通信したいと思います。この場合、古いものを使用しますが、間にアダプターを配置する必要がある場合があります。理想的には、クライアントコードで、次のようなことを行います。
IServiceOld client = *Magic*
client.DoWork();
この場合の魔法は、次のような単純なファクトリです。
internal class ClientFactory
{
public IServiceOld GetClient()
{
string service = ConfigurationManager.AppSettings["Service"];
if(service == "Old")
return new ClientOld();
else if(service == "New")
return new ClientNew();
throw new NotImplementedException();
}
}
使用するクライアントの決定をapp.configに委任しましたが、そこにバージョンチェックを挿入できます。ClientOldの実装は、IServiceOldの通常のWCFクライアントです。
public class ClientOld : IServiceOld
{
private IServiceOld m_Client;
public ClientOld()
{
var factory = new ChannelFactory<IServiceOld>(new WSHttpBinding(), "http://localhost:8732/test/old");
m_Client = factory.CreateChannel();
}
public void DoWork()
{
m_Client.DoWork();
}
...
}
ClientNewは、代わりに、私たちが望んでいた動作、つまりDoAdditionalWork操作の呼び出しを実装します。
public class ClientNew : IServiceOld
{
private IServiceNew m_Client;
public ClientNew()
{
var factory = new ChannelFactory<IServiceNew>(new WSHttpBinding(), "http://localhost:8732/test/new");
m_Client = factory.CreateChannel();
}
public void DoWork()
{
m_Client.DoWork();
m_Client.DoAdditionalWork();
}
...
}
これで、次の例のようにクライアントを使用できるようになりました。
var client = new ClientFactory().GetClient();
client.DoWork();
私たちは何を達成しましたか?クライアントを使用するコードは、実際のWCFクライアントが実行する必要のある追加の作業から抽象化され、使用するクライアントに関する決定はファクトリに委任されます。このサンプルのバリエーション/拡張があなたのニーズに合うことを願っています。