我对ServiceStack' old'和'新' API需要一些澄清和最佳实践,尤其是请求/响应DTO和路由。我在Pluralsight上观看了一些课程,并在我的电子书架上的servicestack.net上听了前三本书。
我喜欢“解决问题”。使用 DDD模式构建的现有应用程序,这意味着我具有高级抽象。客户端是WPF并遵循MVVM模式。我有客户端服务'服务器端服务'和存储库类(以及一些聚合)。我使用NHibernate 4(使用流畅的API和代码优先的方法)作为ORM。只有我的存储库类知道ORM。我有所有Entity对象的DTO,在我的WPF客户端中,我只使用ViewModel类中的那些DTO。我大量使用AutoMapper来转移'实体反对我的DTO,反之亦然。
我的困惑始于这些DTO以及ServiceStack中使用的请求/响应DTO。这是一个非常简化的地址实体示例,它说明了问题:
我的所有实体对象都派生自EntityBase,其中包含所有实体中使用的基本属性:
public abstract class EntityBase : IEntity
{
public virtual Guid Id { get; protected set; }
public virtual DateTime CDate { get; set; } //creation date
public virtual string CUser { get; set; } //creation user
public virtual DateTime MDate { get; set; } //last modification date
public virtual string MUser { get; set; } //last modification user
//
// some operators and helper methods irrelevant for the question
// ....
}
public class Address : EntityBase
{
public string Street { get; private set; }
public string AdrInfo1 { get; private set; }
public string AdrInfo2 { get; private set; }
public string ZipCode { get; private set; }
public string City { get; private set; }
public string Country { get; private set; }
}
当然有相关对象的集合和引用在这里被忽略,以及数据库映射器,命名约定等。我看起来像DTO:
public class AddressDto
{
public Guid Id { get; set; } // NHibernate GUID.comb, NO autoincrement ints!!
public DateTime CDate { get; set; }
public string CUser { get; set; }
public DateTime MDate { get; set; }
public string MUser { get; set; }
public string Street { get; private set; }
public string AdrInfo1 { get; private set; }
public string AdrInfo2 { get; private set; }
public string ZipCode { get; private set; }
public string City { get; private set; }
public string Country { get; private set; }
}
要在ServiceStack中使用它,我需要支持以下内容:
所以我的地址服务'应该有以下方法:
对我而言,很明显,所有请求都返回单个AddressDto
或List<AddressDto>
作为ResponseDTO,除了应该只返回状态对象的删除。
但是如何定义所有这些RequestDTO?我是否真的必须为每个场景定义一个DTO?在书中我只看到了样本:
[Route("/addresses", "GET")]
public class GetAddresses : IReturn<AddressesResponse> { }
[Route("/addresses/{Id}", "GET")]
public class GetAddressById : IReturn<AddressResponse>
{
public Guid Id { get; set; }
}
[Route("/addresses/{City}", "GET")]
public class GetAddressByCity : IReturn<AddressResponse>
{
public string City { get; set; }
}
// .... etc.
这是很多样板代码,并且记得我在C ++和CORBA中使用了很多旧的IDL编译器......
特别是对于创建和更新,我应该能够分享&#39;一个DTO甚至更好地重用我现有的DTO ......对于删除,可能没有多少选择......
然后过滤器。我有其他DTO具有更多属性。像WCF,RPC等中使用的函数方法是代码...... 在我的存储库中,我传递整个DTO并使用谓词构建器类,该类根据填充的属性组成LINQ where子句。这看起来像这样:
List<AddressDto> addresses;
Expression<Func<Address, bool>> filter = PredicateBuilder.True<Address>();
if (!string.IsNullOrEmpty(address.Zip))
filter = filter.And(s => s.Zip == address.Zip);
// .... etc check all properties and dynamically build the filter
addresses = NhSession.Query<Address>()
.Where(filter)
.Select(a => new AddressDto
{
Id = a.Id,
CDate = a.CDate,
//.... etc
}).ToList();
我的RequestDTO可以做什么类似的事情以及如何定义路由?
答案 0 :(得分:1)
以下现有的相关答案涵盖了此处提出的许多问题。请求/响应DTO是您define your Service Contract使用的,即代替您的服务接受的using RPC method signatures, you define your contract with messages(请求DTO)并返回(响应DTO)。前面的示例还介绍了guidelines on designing HTTP APIs with ServicesStack。
使用well-defined DTOs have a very important role in Services:
您希望确保您的服务返回的所有类型都在DTO中,以及您的服务所在的基本网址您需要的所有 ,以便您的服务使用者了解为了使用您的服务。他们可以与任何.NET服务客户端一起使用获得没有代码生成的端到端Typed API ,工具或任何其他人工机器。
DTO定义了您的服务合同,使它们与任何服务器实现隔离,就是您的服务如何能够封装其功能(可能具有无限复杂性)并使其在远程外观后面可用。它将您的服务提供的内容与实现它的复杂性分开。它定义了服务的API,并告诉服务使用者他们需要知道的最少信息,以发现服务提供的功能以及如何使用它们(在C / C ++源代码中维护类似于Header文件的角色)。明确定义的服务合同与实现分离,强制实施互操作性,确保您的服务不要求特定的客户端实现,确保任何平台上的任何HTTP客户端都可以使用它们。 DTO还定义了服务有线格式的形状和结构,确保它们可以干净地反序列化为本机数据结构,从而消除了手动解析服务响应的工作量。
如果您正在进行大量数据驱动的服务,我建议您查看AutoQuery,这样您就可以使用服务请求DTO定义来定义完全可查询的服务而无需实现。