WCF:読み取り専用の DataMember プロパティを設定せずに公開しますか?
-
18-09-2019 - |
質問
[DataContract] を通じてクライアント側で利用できるようにするサーバー側クラスがあります。このクラスには読み取り専用フィールドがあり、プロパティを通じて使用できるようにしたいと考えています。ただし、get と set の両方を持たないと [DataMember] プロパティを追加することはできないようです。
それで、セッターなしで [DataMember] プロパティを持つ方法はありますか?
[DataContract]
class SomeClass
{
private readonly int _id;
public SomeClass() { .. }
[DataMember]
public int Id { get { return _id; } }
[DataMember]
public string SomeString { get; set; }
}
それとも、ソリューションは [DataMember] をフィールドとして使用しますか? (例:示されている ここ)?これも試してみましたが、フィールドが読み取り専用であることは関係ないようです...?
編集:このようにハッキングして読み取り専用プロパティを作成する唯一の方法はありますか?(いや、そんなことはしたくないんですが…)
[DataMember]
public int Id
{
get { return _id; }
private set { /* NOOP */ }
}
解決
実際には、「サーバー側」クラスはクライアントに「利用可能」にはなりません。
何が起こるかというと、次のとおりです。データ コントラクトに基づいて、クライアントはサービスの XML スキーマから新しい別のクラスを作成します。それ できない サーバー側クラス自体を使用してください。
XML スキーマ定義から新しいクラスを再作成しますが、そのスキーマには可視性やアクセス修飾子などの .NET 固有のものは含まれていません。結局のところ、それは単なる XML スキーマです。クライアント側クラスは、ネットワーク上に同じ「フットプリント」を持つように作成されます。基本的には同じ XML 形式にシリアル化されます。
あなた できない 標準の SOAP ベースのサービスを通じて、クラスに関する .NET 固有のノウハウを「転送」します。結局のところ、渡しているのは シリアル化されたメッセージ - 授業はありません!
「SOA の 4 つの原則」(Microsoft の Don Box によって定義) を確認してください。
- 境界は明確です
- サービスは自律的です
- サービスはクラスではなくスキーマとコントラクトを共有します
- 互換性はポリシーに基づいています
ポイント #3 を参照してください - サービスはスキーマとコントラクトを共有します。 ない クラス - データ コントラクトのインターフェイスと XML スキーマのみを共有します - それだけです - .NET クラスはありません。
他のヒント
プロパティではありません、フィールド上のデータメンバーの属性を入れます。
は、WCFは、カプセル化を知らないという考えを、覚えておいてください。カプセル化は、OOPの用語ではなく、SOAの用語です。
フィールドは、あなたのクラスを使用している人々のために読み取り専用になることを覚えて、言った - サービスを使用して、誰もが彼らの側にフィールドへのフルアクセスを持つことになります。
。私はシルバーに上を通過したかった私のサービス層にクラスのいくつかの特性を有していました。私は、全く新しいクラスを作成する必要はありませんでした。
本当に「推奨」されないが、これは(単に視覚的なデータバインディングのため)のSilverlightにTotal
プロパティ上を通過するために、2つの悪の低いように見えました。
public class PricingSummary
{
public int TotalItemCount { get; set; } // doesnt ideally belong here but used by top bar when out of store area
public decimal SubTotal { get; set; }
public decimal? Taxes { get; set; }
public decimal Discount { get; set; }
public decimal? ShippingTotal { get; set; }
public decimal Total
{
get
{
return + SubTotal
+ (ShippingTotal ?? 0)
+ (Taxes ?? 0)
- Discount;
}
set
{
throw new ApplicationException("Cannot be set");
}
}
}
これを達成する方法があります。ただし、これは、次の原則に直接違反することに注意してください。 この答え:
「3.サービスはクラスではなくスキーマとコントラクトを共有します。」
この違反が問題にならない場合は、次のようにします。
サービスとデータ コントラクトを別の (ポータブル) クラス ライブラリに移動します。(このアセンブリを呼び出しましょう
SomeService.Contracts
.) これは、不変を定義する方法です。[DataContract]
クラス:namespace SomeService.Contracts { [DataContract] public sealed class Foo { public Foo(int x) { this.x = x; } public int X { get { return x; } } [DataMember] // NB: applied to the backing field, not to the property! private readonly int x; } }
ご了承ください
[DataMember]
バッキングフィールドに適用され、 ない 対応する読み取り専用プロパティに追加します。両方のサービス アプリケーション プロジェクトからコントラクト アセンブリを参照します (私のものをプロジェクトと呼びます)
SomeService.Web
)そしてあなたのクライアントプロジェクトから(私のプロジェクトはSomeService.Client
)。これにより、ソリューション内で次のようなプロジェクトの依存関係が発生する可能性があります。次に、サービス参照をクライアント プロジェクトに追加するときに、「タイプを再利用する」オプションが有効になっていることを確認し、コントラクト アセンブリ (
SomeService.Contracts
) がこれに含まれます:
ほら!Visual Studio では、新しいファイルを生成する代わりに、 Foo
サービスの WSDL スキーマからの型は、不変の型を再利用します。 Foo
契約アセンブリから入力します。
最後の警告:あなたはすでに、で引用されているサービス原則から逸脱しています。 他の答えは. 。しかし、これ以上迷わないようにしてください。データ コントラクト クラスに (ビジネス) ロジックを追加したくなるかもしれません。しないでください。これらは、管理できる限りダム データ転送オブジェクト (DTO) の近くに置く必要があります。
クラスを使用して契約を実装する前に、サービス契約(Interface)を定義します。