使用ServiceStack进行CRUD和查询 - 需要摆脱一些混淆

时间:2016-09-29 10:24:30

标签: servicestack

我对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中使用它,我需要支持以下内容:

  1. CRUD功能
  2. 过滤/搜索功能
  3. 所以我的地址服务'应该有以下方法:

    • GetAddresses(ALL,ById,ByZip,ByCountry,ByCity)
    • AddAddress(没有Id.Cart的完整AddressDTO,CUser在没有用户输入的情况下自动填充)
    • UpdateAddress(完整的AddressDTO没有CUser和CDate,MDate和MUser在没有用户输入的情况下自动填充)
    • DeleteAddress(只是ID)

    对我而言,很明显,所有请求都返回单个AddressDtoList<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可以做什么类似的事情以及如何定义路由?

1 个答案:

答案 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定义来定义完全可查询的服务而无需实现。