質問
私は今グーグルで何時間もグーグルで読んでいます、そして、私の特定のシナリオを扱う人を見つけることができません...
WCFサービス契約のインターフェイスを使用して、ワイヤーの各端で使用されるクラスのサービスを大まかに結合したいと考えています。これにより、コンサルタントに渡すことができるサービス契約とデータ契約(インターフェイスのみ)のみを含む低レベルのアセンブリを持つことができます。ワイヤーの端で、データ契約インターフェイスを実装するデータクラスをインスタンス化し、ワイヤーを介して送信することができます。 私たちの 同じインターフェイスを実装するデータクラスのバージョン。
これが例です。 IDataContract
ワイヤーを介して送信したい裸の情報が含まれています。エンドポイントおよびその他のWCF固有の構成はすべてデフォルトのものです(私の問題はその中にある可能性があるため、物事を変更する必要がある場合は、さらに多くを含めることができます)。
編集: :私はコードをもっと含め、いくつかのクラスを変更して、それがあまり混乱していないのを助けました。 DataContractAttributesへの名前と名前空間の追加、および構成ファイルの2つのセクションは、からの情報に基づいた新しい追加です このブログ投稿. 。インターフェイスの代わりに抽象的なベースクラスに切り替えた場合、 できます. 。ただし、可能であれば、これをインターフェイスで動作させたいと思います。
共有ライブラリ(私のコード、クライアントの著者と共有):
public interface IDataContract
{
string MyProperty { get; set; }
}
[ServiceContract]
public interface ITestService
{
[OperationContract]
IDataContract TestSharedInterface(IDataContract clientData);
}
クライアントコード(彼らの):
[DataContract(Name = "IDataContract", Namespace = "http://services.sliderhouserules.com")]
public class ClientDataClass : IDataContract
{
[DataMember]
public string MyProperty { get; set; }
}
private static void CallTestSharedInterface()
{
EndpointAddress address = new EndpointAddress("http://localhost/ServiceContractsTest.WcfService/TestService.svc");
ChannelFactory<ITestService> factory = new ChannelFactory<ITestService>("ITestService", address);
ITestService proxy = factory.CreateChannel();
((IClientChannel)proxy).Open();
IDataContract clientData = new ClientDataClass() { MyProperty = "client data" };
IDataContract serverData = proxy.TestSharedInterface(clientData);
MessageBox.Show(serverData.MyProperty);
}
クライアント構成:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="ServiceContractsTest.Contracts.DataContracts.IDataContract, ServiceContractsTest.Contracts">
<knownType type="ServiceContractsTest.WcfClient.ClientDataClass, ServiceContractsTest.WcfClient"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
サーバーコード(鉱山):
public class TestService : ITestService
{
public IDataContract TestSharedInterface(IDataContract clientData)
{
ServerDataClass convertedClientData = (ServerDataClass)clientData;
IDataContract serverData = new ServerDataClass() { MyProperty = convertedClientData.MyProperty + " + server data added" };
return serverData;
}
}
[DataContract(Name = "IDataContract", Namespace = "http://services.sliderhouserules.com")]
public class ServerDataClass : IDataContract
{
[DataMember]
public string MyProperty { get; set; }
}
サーバー構成:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="ServiceContractsTest.Contracts.DataContracts.IDataContract, ServiceContractsTest.Contracts">
<knownType type="ServiceContractsTest.WcfService.ServerDataClass, ServiceContractsTest.WcfService"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
既知のタイプについて不平を言うクライアントコールでシリアル化エラーが発生しています。そのクライアントクラスでいくつかのメタデータマークアップが足りないのですか?私は、問題をどこに知っているかさえどこにあるかについて途方に暮れています。私が考えることができるすべての検索を試してみたので、誰もこの特定のシナリオに対処していないようです。
基本的に、私は欲しいです ClientDataClass
にシリアル化する <IDataContract><MyProperty>client data</MyProperty></IDataContract>
そして、それをaにゆるくすることができます ServerDataClass
実例。これは可能であるべきだと思われます。
解決
データ契約がインターフェイスの場合、WCFは、着信リクエストのためにどのオブジェクトをインスタンス化するかを知ることができません。すべてのADDサービスリファレンスがWSDLを読み取り、WSDLのタイプ情報に基づいて新しいクラスを生成した後、クラスがサービスと同じである必要はありません。
他のヒント
このブログは、私の問題の解決策を見つけるための正しい方向を与えてくれます。実際、私はまったく同じシナリオを持っています SliderHouserules 彼の投稿で説明しています。
しかし、私のシナリオでは、抽象クラスやベースクラスを使用することはできません。そこで、Typeshelperクラスを使用して、DataContractSerializerセクションを自分で読み取り、関連するタイプをWCFサービスに渡しました。
namespace ExampleNamespace
{
public interface IJustAInstance { }
[ServiceContract]
[ServiceKnownType("GetKnownTypes", typeof(ExampleNamespace.TypesHelper))]
public interface ICreateInstance
{
IJustAInstance CreateInstance();
}
public static class TypesHelper
{
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
DataContractSerializerSection section = (DataContractSerializerSection)
ConfigurationManager.GetSection(
"system.runtime.serialization/dataContractSerializer");
if (dataContractSerializerSection != null)
{
foreach (DeclaredTypeElement item in dataContractSerializerSection.DeclaredTypes)
{
foreach (TypeElement innterItem in item.KnownTypes)
{
Type type = Type.GetType(innterItem.Type);
if (typeof(IJustAInstance).IsAssignableFrom(type ))
yield return type;
}
}
}
}
}
}
クライアントコントラクトとサーバーコントラクトが(プロパティとして)提供できるベースコントラクトを作成し、クライアントコントラクトまたはサーバーコントラクトの新しいインスタンスを作成するときにそれぞれのコンストラクターで使用できることを作成できます。その後、共有Libにベースコントラクトを追加するだけです。