在ServiceStack的examples中,我没有看到首先是ASP.NET MVC网站的单个应用程序,然后将ServiceStack服务放在第二位。
让我们看一个非常简单的ASP.NET MVC Web应用程序,它通过Views呈现产品。它使用控制器,视图,模型和视图模型。
假设我们有一个Product
模型,它会被持久存储到文档数据库中。假设我们有一个ProductViewModel
的视图模型,它从Product
映射并显示在MVC Razor View / PartialView中。
所以这是一个网络方面的东西..现在我们假设我们想要添加一个服务,将产品返回到各种客户端,如Windows 8应用程序。
请求/响应类是否应该与我们已有的完全断开连接?我们的ProductViewModel
可能已包含我们要从服务中返回的所有内容。
由于我们已经有Product
(模型类),我们不能在API命名空间中拥有另一个Product
类。我们可以但是这会让事情变得不清楚,我想避免这样做
那么,我们是否应该在API命名空间中引入独立的ProductRequest
类和ProductRequestResponse
(继承ProductViewModel)类?
像ProductRequestResponse : ProductViewModel
一样?
我所说的是,我们已经拥有了Model和ViewModel类,并为SS服务构建了Request和Response类,我们必须创建另外两个文件,主要是通过复制我们已有的类中的所有内容。这对我来说看起来并不干净,它可能会遵循关注点分离指南,但DRY也很重要,实际上不仅仅是分离所有内容(将所有内容分开导致代码重复)。
我希望看到的是已经制作了Web应用程序的情况,它目前具有模型和ViewModel,并返回适当的视图以便在Web上显示,但可以扩展为功能齐全的服务以支持程序化客户端?像AJAX客户一样...与我们已经拥有的一样。
另一件事:
您将看到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;
}
}
答案 0 :(得分:69)
您可以在整个系统中创建的最重要的界面是面向外部的服务合同,这是您的服务或应用程序的消费者将绑定到的,即通常无法获得的现有呼叫站点与您的代码库一起更新 - 每个其他模型都是次要的。
在跟踪远程服务的Martin Fowler's recommendation for using DTOs(数据传输对象)(MSDN)之后,ServiceStack鼓励使用干净,无污染的POCO来定义明确定义的合同。保持在一个很大的实现和无依赖.dll。这样做的好处使您能够在C#/.NET clients中重复使用用于定义服务的类型DTO,从而在不使用任何代码的情况下提供端到端类型的API - gen或其他人工机械。
保持干燥不应与明确说明意图混淆,你应该避免尝试干燥或hide behind inheritance,魔法属性或任何其他机制。拥有干净,定义明确的DTO提供了一个参考源,任何人都可以查看每个服务接受和返回的内容,它允许您的客户端和服务器开发人员立即开始工作并绑定到外部服务模型而无需实现写完了。
保持DTO分离还使您可以自由地从内部重新分配实施,而不会破坏外部客户端,即您的服务开始缓存响应或利用NoSQL解决方案来填充您的响应。
它还提供了用于创建自动生成的元数据页面,示例响应,Swagger支持,XSD的权威来源(未在应用逻辑中泄露或耦合) ,WSDL等。
虽然我们鼓励保留单独的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();
答案 1 :(得分:2)
如果您没有专门针对ServiceStack,只想要“全功能服务来支持程序化客户......使用我们已有的”,您可以尝试以下操作:让您的控制器返回ViewResult
或JsonResult
基于请求的接受标头 - Request.AcceptTypes.Contains("text/html")
或Request.AcceptTypes.Contains("application/json")
。
ViewResult
和JsonResult
都是ActionResult
,因此操作的签名保持不变,View()
和Json()
都接受ViewModel。此外,如果您有一个ControllerBase,您可以创建一个调用View()或Json()的基本方法(例如protected ActionResult RespondWith(Object viewModel)
),以便对现有代码的更改最小。
当然,如果你的ViewModel不是纯粹的(即有一些特定于html的东西,或者你依赖一些ViewBag魔法),那么它的工作量会更多。并且您不会获得ServiceStack提供的SOAP或其他绑定类型,但如果您的目标是支持JSON数据接口,而对现有MVC应用程序的代码更改最少,那么这可能是一个解决方案。
的Lp