28

在 ServiceStack 的示例中,我没有看到一个应用程序首先是 ASP.NET MVC 网站,然后是 ServiceStack 服务。

让我们看一个非常简单的 ASP.NET MVC Web 应用程序,它通过 Views 呈现产品。它使用控制器、视图、模型和视图模型。

假设我们有一个模型Product被持久化到一个文档数据库中。假设我们有一个从MVC Razor View/PartialViewProductViewModel映射并显示的视图模型。Product

所以这是网络方面的事情。现在假设我们想要向各种客户端(如 Windows 8 应用程序)添加返回产品的服务。

请求/响应类是否应该与我们已有的完全断开?我们ProductViewModel可能已经包含了我们想要从服务返回的所有内容。

由于我们已经有了Product(模型类),我们不能Product在 API 命名空间中有另一个类。我们可以,但这会让事情变得不清楚,我想避免这种情况。

那么,我们是否应该在 API 命名空间中引入独立ProductRequest类和ProductRequestResponse(继承 ProductViewModel)类?

像这样ProductRequestResponse : ProductViewModel

我要说的是,我们已经有了 Model 和 ViewModel 类,并且要为 SS 服务构建 Request 和 Response 类,我们必须创建另外两个文件,主要是通过从我们已有的类中复制所有内容。这在我看来并不 DRY,它可能遵循关注点分离准则,但 DRY 也很重要,实际上不仅仅是分离所有内容(分离所有内容会导致代码重复)。

我想看到的是一个已经制作了 Web 应用程序的情况,它当前具有模型和视图模型并返回适当的视图以在 Web 上显示,但可以扩展为功能齐全的服务以支持程序化客户端?像 AJAX 客户端等......我们已经拥有的。

另一件事:

如果你看看这个例子https://github.com/ServiceStack/ServiceStack.Examples/blob/master/src/ServiceStack.MovieRest/MovieService.cs

你会看到有Movie请求类和Movies请求类(一个用于单个电影请求,另一个用于电影列表)。因此,还有两个服务,MovieService一个MoviesService处理对单个电影的请求,另一个处理一个类型的电影。

现在,虽然我喜欢 SS 服务方法并且我认为它是正确的方法,但我不喜欢这种仅仅因为请求类型的分离。如果我想要导演的电影怎么办?我会为它发明另一个具有Director属性和另一个服务(MoviesByDirector)的请求类吗?

我认为样本应该面向一项服务。与电影打交道的一切都需要集中在一个屋檐下。如何使用 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;
    }
}
4

2 回答 2

70

服务层是你最重要的契约

您可以在整个系统中创建的最重要的接口是面向外部的服务合同,这是您的服务或应用程序的消费者将绑定到的,即通常不会与您的代码一起更新的现有调用站点-base - 其他所有模型都是次要的。

DTO 是远程服务的最佳实践

遵循Martin Fowler对远程服务 ( MSDN ) 使用 DTO (数据传输对象)的建议,ServiceStack鼓励使用干净、无污染的 POCO 来定义定义良好的合同,该合同应在很大程度上保持在实现中且无依赖。 dll。这样做的好处是,您可以在C#/.NET 客户端中重新使用用于定义服务的类型化 DTO——提供端到端类型化 API,而无需使用任何代码—— gen 或其他人造机械。

干燥与意图

保持 DRY 不应与明确说明意图相混淆,您应该避免尝试 DRY 或隐藏在继承、魔术属性或任何其他机制后面。拥有干净、定义明确的 DTO 提供了一个单一的参考来源,任何人都可以查看每个服务接受和返回的内容,它允许您的客户端和服务器开发人员立即开始他们的工作并绑定到外部服务模型而无需实现被写了。

保持 DTO 分离还可以让您在不破坏外部客户端的情况下自由地从内部重构实现,即您的服务开始缓存响应或利用 NoSQL 解决方案来填充您的响应。

它还提供了用于创建自动生成的元数据页面、示例响应、Swagger 支持、XSD、WSDL 等的权威源(不会泄露或耦合到您的应用程序逻辑中)。

使用 ServiceStack 的内置自动映射

虽然我们鼓励保留单独的 DTO 模型,但您不需要维护自己的手动映射,因为您可以使用AutoMapper之类的映射器或使用 ServiceStack 的内置自动映射支持,例如:

创建一个新的 DTO 实例,在 viewModel 上填充匹配的属性:

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

初始化 DTO 并在视图模型上使用匹配的属性填充它:

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

初始化 DTO 并在视图模型上使用非默认匹配属性填充它:

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

初始化 DTO 并使用在视图模型上用Attr属性注释的匹配属性填充它:

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

当映射逻辑变得更加复杂时,我们喜欢使用扩展方法来保持代码 DRY 并将映射维护在一个可以在您的应用程序中轻松使用的地方,例如:

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();
于 2013-03-12T18:54:35.040 回答
2

如果您没有专门与 ServiceStack 绑定,只是想要“功能齐全的服务来支持编程客户端......我们已经拥有的”,您可以尝试以下操作:让您的控制器根据请求的接受标头返回 aViewResult或 a -或。 JsonResultRequest.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 魔法),那么它的工作量就更大了。而且您不会获得 ServiceStack 提供的 SOAP 或其他绑定类型,但如果您的目标是支持 JSON 数据接口,而对现有 MVC 应用程序的代码更改最少,那么这可能是一个解决方案。

脂蛋白

于 2013-04-10T22:36:50.610 回答