【问题标题】:Does ServiceStack support generics in end-to-end typed requestsServiceStack 是否支持端到端类型化请求中的泛型
【发布时间】:2013-04-11 03:40:16
【问题描述】:

我在玩 ServiceStack,想知道它是否支持这种情况。我在我的请求类型中使用了泛型,因此许多从公共接口继承的 DTO 将支持相同的基本方法 [比如... GetById(int Id)]。

使用特定于一种 DTO 的请求类型可以工作,但会破坏泛型的优点...

var fetchedPerson = client.Get<PersonDto>(new PersonDtoGetById() { Id = person.Id });
Assert.That(person.Id, Is.EqualTo(fetchedPerson.Id)); //PASS

将路由映射到泛型也可以:

Routes.Add<DtoGetById<PersonDto>>("/persons/{Id}", ApplyTo.Get);
...
var fetchedPerson2 = client.Get<PersonDto>(string.Format("/persons/{0}", person.Id));
Assert.That(person.Id, Is.EqualTo(fetchedPerson2.Id)); //PASS

但使用端到端通用请求类型失败:

var fetchedPerson3 = client.Get<PersonDto>(new DtoGetById<PersonDto>() { Id = person.Id });
Assert.That(person.Id, Is.EqualTo(fetchedPerson3.Id)); //FAIL

我想知道我是否只是遗漏了一些东西,或者我是否试图将 ooone 层抽象得太远...... :)

下面是一个完整的,使用 NUnit 的失败程序,默认的 ServiceStack 东西:

namespace ssgenerics
{
    using NUnit.Framework;
    using ServiceStack.ServiceClient.Web;
    using ServiceStack.ServiceHost;
    using ServiceStack.ServiceInterface;
    using ServiceStack.WebHost.Endpoints;

    [TestFixture]
    class Program
    {
        public static PersonDto GetNewTestPersonDto()
        {
            return new PersonDto()
            {
                Id = 123,
                Name = "Joe Blow",
                Occupation = "Software Developer"
            };
        }

        static void Main(string[] args)
        {}

        [Test]
        public void TestPutGet()
        {
            var listeningOn = "http://*:1337/";
            var appHost = new AppHost();
            appHost.Init();
            appHost.Start(listeningOn);
            try
            {

                var BaseUri = "http://localhost:1337/";
                var client = new JsvServiceClient(BaseUri);

                var person = GetNewTestPersonDto();
                client.Put(person);

                var fetchedPerson = client.Get<PersonDto>(new PersonDtoGetById() { Id = person.Id });
                Assert.That(person.Id, Is.EqualTo(fetchedPerson.Id));

                var fetchedPerson2 = client.Get<PersonDto>(string.Format("/persons/{0}", person.Id));
                Assert.That(person.Id, Is.EqualTo(fetchedPerson2.Id));
                Assert.That(person.Name, Is.EqualTo(fetchedPerson2.Name));
                Assert.That(person.Occupation, Is.EqualTo(fetchedPerson2.Occupation));

                var fetchedPerson3 = client.Get<PersonDto>(new DtoGetById<PersonDto>() { Id = person.Id });
                Assert.That(person.Id, Is.EqualTo(fetchedPerson3.Id));
                Assert.That(person.Name, Is.EqualTo(fetchedPerson3.Name));
                Assert.That(person.Occupation, Is.EqualTo(fetchedPerson3.Occupation));
            }
            finally
            {
                appHost.Stop();
            }
        }
    }

    public interface IDto : IReturnVoid
    {
        int Id { get; set; }
    }

    public class PersonDto : IDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Occupation { get; set; }
    }

    public class DtoGetById<T> : IReturn<T> where T : IDto { public int Id { get; set; } }
    public class PersonDtoGetById : IReturn<PersonDto> { public int Id { get; set; } }

    public abstract class DtoService<T> : Service where T : IDto
    {
        public abstract T Get(DtoGetById<T> Id);
        public abstract void Put(T putter);
    }

    public class PersonService : DtoService<PersonDto>
    {
        public override PersonDto Get(DtoGetById<PersonDto> Id)
        {
            //--would retrieve from data persistence layer
            return Program.GetNewTestPersonDto();
        }

        public PersonDto Get(PersonDtoGetById Id)
        {
            return Program.GetNewTestPersonDto();
        }

        public override void Put(PersonDto putter)
        {
            //--would persist to data persistence layer
        }
    }

    public class AppHost : AppHostHttpListenerBase
    {
        public AppHost()
            : base("Test HttpListener",
                typeof(PersonService).Assembly
                ) { }

        public override void Configure(Funq.Container container)
        {
            Routes.Add<DtoGetById<PersonDto>>("/persons/{Id}", ApplyTo.Get);
        }
    }
}

【问题讨论】:

    标签: servicestack


    【解决方案1】:

    不,ServiceStack 中的一个基本概念是,每个服务都需要自己的唯一请求 DTO,请参阅this answer for more examples on this

    你可以这样做:

    [Route("/persons/{Id}", "GET")]
    public class Persons : DtoGetById<Person> { ... }   
    

    但我强烈advise against using inheritance in DTOs。属性声明就像服务合同的 DSL,它不应该被隐藏。

    更多详情请看这个答案on the purpose of DTO's in Services

    【讨论】:

    • 好的。我会尽量坚持最佳实践。我只是希望在我的客户端代码中避免尽可能多的样板代码。让 DTO 实现接口是否符合规定?
    • 是的,DTO 上的接口能够跨类似 DTO 提供通用功能优于继承。
    • 感谢 Demis,ServiceStack 非常棒。在 .NET Rocks 上听说过它。继续努力!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-15
    • 1970-01-01
    • 2016-09-18
    • 1970-01-01
    • 2014-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多