web-dev-qa-db-ja.com

ServiceStackの新しいサービスを並べてASP.NET MVC Webサイト

ServiceStackの examples では、最初にASP.NET MVC Webサイトで、次にServiceStackサービスを作成する単一のアプリケーションが表示されません。

ビューを通じて製品をレンダリングする非常にシンプルなASP.NET MVC Webアプリケーションを見てみましょう。コントローラー、ビュー、モデル、ビューモデルを使用します。

ドキュメントDBに永続化されるProductのモデルがあるとします。 ProductViewModelからマップされ、MVC Razor View/PartialView内に表示されるProductのビューモデルがあるとします。

これはWebの側面です。ここで、Windows 8アプリケーションなどのさまざまなクライアントに製品を返すサービスを追加するとします。

要求/応答クラスは、すでに持っているものから完全に切り離されるべきですか? ProductViewModelには、サービスから返したいすべてのものがすでに含まれている可能性があります。

すでにProduct(モデルクラス)があるので、API名前空間に別のProductクラスを置くことはできません。まあ、そうすることはできますが、不明確になるので避けたいです。

では、スタンドアロンのProductRequestクラスとProductRequestResponse(ProductViewModelを継承)クラスをAPI名前空間に導入する必要がありますか?

そのようです ProductRequestResponse : ProductViewModel

私が言っていることは、ModelクラスとViewModelクラスが既にあり、SSサービスのRequestクラスとResponseクラスを構築するには、ほとんどの場合、すでに持っているクラスからすべてをコピーすることによって、別の2つのファイルを作成する必要があります。これは見えないDRY私には、懸念の分離のガイドラインに従うかもしれませんが、DRYも重要です。実際にはすべてを分離するよりもコードの複製)。

私が見たいのは、Webアプリケーションがすでに作成されている場合です。現在、ModelsとViewModelsを備えており、Webでの表示に適切なビューを返しますが、完全に機能するサービスに拡張して、プログラムによるクライアントをサポートできますか? AJAXクライアントなど)のように、すでに持っているものを使います。

別物:

この例を見てみると https://github.com/ServiceStack/ServiceStack.Examples/blob/master/src/ServiceStack.MovieRest/MovieService.cs

Movie要求クラスとMovies要求クラス(1つは単一の映画要求用、もう1つは映画のリスト用)があることがわかります。そのため、MovieServiceMoviesServiceの2つのサービスもあります。1つは単一の映画のリクエストを処理し、もう1つはジャンルの映画を処理します。

さて、私はサービスへのSSのアプローチが好きで、それは正しいものだと思いますが、要求のタイプのためだけに、この種の分離は好きではありません。監督の映画が欲しかったら? Directorプロパティを持つ別のリクエストクラスと、そのための別のサービス(MoviesByDirector)を発明しますか?

サンプルは1つのサービスに向けられるべきだと思います。映画を扱うために必要なものはすべて1つの屋根の下にある必要があります。 ServiceStackでそれを実現するにはどうすればよいですか?

public class ProductsService : Service
{
    private readonly IDocumentSession _session;
    private readonly ProductsHelperService _productsHelperService;
    private readonly ProductCategorizationHelperService _productCategorization;

    public class ProductRequest : IReturn<ProductRequestResponse>
    {
        public int Id { get; set; }
    }

    // Does this make sense? 
    // Please note, we use ProductViewModel in our Views and it holds everything we'd want in service response also
    public class ProductRequestResponse : ProductViewModel
    {
    }

    public ProductRequestResponse GetProducts(ProductRequest request)
    {
        ProductRequestResponse response = null;
        if (request.Id >= 0)
        {
            var product = _session.Load<Product>(request.Id);
            response.InjectFrom(product);
        }
        return response;
    }
}
28
mare

サービス層はあなたの最も重要な契約です

システム全体でこれまでに作成できる最も重要なインターフェイスは、外部向けのサービスコントラクトです。これは、サービスまたはアプリケーションのコンシューマーがバインドするものです。つまり、既存の呼び出しサイトがコードとともに更新されないことがよくあります。 -base-他のすべてのモデルはセカンダリです。

DTOはリモートサービスのベストプラクティスです

次の Martin FowlerによるDTOの使用に関する推奨事項 (データ転送オブジェクト)のリモートサービス( [〜#〜] msdn [〜#〜] )、 ServiceStack クリーンで汚染されていないPOCOを使用して、明確に定義されたコントラクトを定義することを奨励します。このコントラクトは、主に実装と依存関係のない.dllに保持する必要があります。 C#/。NETクライアント -エンドツーエンドの型指定されたAPIを提供することで、サービスの定義に使用される型指定されたDTOをそのまま再利用できるという利点がありますコード生成やその他の人工機械を使用しません。

DRY vs Intent

物事を維持するDRY意図を明確に述べることと混同しないでください。これは、DRYまたは 継承の背後に隠す 、マジックプロパティまたはその他のメカニズム。クリーンで明確に定義されたDTOがあれば、各サービスが受け入れて返すものを誰でも確認できる単一の参照ソースが提供され、クライアントとサーバーの開発者はすぐに作業を開始してバインドできます。実装が記述されていない外部サービスモデル。

また、DTOを分離しておくと、外部クライアントを壊すことなく、実装を内部からリファクタリングできます。つまり、サービスは応答のキャッシュを開始するか、NoSQLソリューションを利用して応答にデータを入力します。

また、自動生成されたメタデータページ、応答例、Swaggerサポート、XSD、WSDLなどの作成に使用される信頼できるソース(アプリロジック内でリークまたは結合されないもの)も提供します。

ServiceStackの組み込みの自動マッピングを使用する

個別のDTOモデルを維持することをお勧めしますが、 AutoMapper のようなマッパーを使用したり、ServiceStackの組み込みの自動マッピングサポートを使用したりできるため、独自の手動マッピングを維持する必要はありません。

ViewModelの一致するプロパティが入力された新しいDTOインスタンスを作成します。

var dto = viewModel.ConvertTo<MyDto>();

DTOを初期化し、ビューモデルの一致するプロパティを設定します。

var dto = new MyDto { A = 1, B = 2 }.PopulateWith(viewModel);

DTOを初期化し、ビューモデルにnon-default一致するプロパティを設定します。

var dto = new MyDto { A = 1, B = 2 }.PopulateWithNonDefaultValues(viewModel);

DTOを初期化し、ビューモデルのAttr属性で注釈が付けられた一致するプロパティを設定します。

var dto = new MyDto { A=1 }.PopulateFromPropertiesWithAttribute<Attr>(viewModel);

マッピングロジックがより複雑になると、拡張メソッドを使用してコードDRYを維持し、アプリケーション内から簡単に利用できる1か所でマッピングを維持します。たとえば、

public static class MappingExtensions
{
    public static MyDto ToDto(this MyViewModel viewModel)
    {
        var dto = viewModel.ConvertTo<MyDto>();
        dto.Items = viewModel.Items.ConvertAll(x => x.ToDto());
        dto.CalculatedProperty = Calculate(viewModel.Seed);
        return dto;
    }
}

これは次のようにして簡単に消費できます。

var dto = viewModel.ToDto();
69
mythz

ServiceStackに特に縛られておらず、「プログラマティッククライアントをサポートするための完全に機能的なサービス...すでに持っているものを使用したい」場合は、次のことを試してください:コントローラーにViewResultまたはJsonResultはリクエストのAcceptヘッダーに基づいています-Request.AcceptTypes.Contains("text/html")またはRequest.AcceptTypes.Contains("application/json")

ViewResultJsonResultはどちらもActionResultであるため、アクションのシグネチャは同じままで、View()Json()はどちらもViewModel。さらに、ControllerBaseがある場合は、View()またはJson()のいずれかを呼び出す基本メソッド(protected ActionResult RespondWith(Object viewModel)など)を作成できるため、既存のコードへの変更は最小限です。

もちろん、ViewModelが純粋でない場合(つまり、html固有のものがあるか、ViewBagのマジックに依存している場合)は、もう少し作業が必要です。また、SOAPまたはServiceStackによって提供されるその他のバインディングタイプは取得しませんが、既存のMVCアプリへのコード変更を最小限に抑えてJSONデータインターフェースをサポートすることが目標である場合、これが解決策になる可能性があります。 。

Lp

2
Boris B.