Does ServiceStack support generics in end-to-end typed requests

喜欢而已 提交于 2019-12-23 21:35:19

问题


I was playin' around with ServiceStack and was wondering if it supported this scenario. I'm using generics in my request types so that many DTOs that inherit from a common interface will support the same basic methods [ like... GetById(int Id) ].

Using a request type specific to a single kind of DTO works, but breaks the generics nice-ness...

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

Mapping a route to the generic also works:

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

But using the end-to-end generic request type fails:

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

I wonder if I'm just missing something, or if i'm trying to abstract just ooone layer too far... :)

Below is a complete, failing program using NUnit, default ServiceStack stuff:

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);
        }
    }
}

回答1:


No, It's a fundamental concept in ServiceStack that each Service requires its own unique Request DTO, see this answer for more examples on this.

You could do:

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

But I strongly advise against using inheritance in DTOs. Property declaration is like a DSL for a service contract and its not something that should be hidden.

For more details see this answer on the purpose of DTO's in Services.



来源:https://stackoverflow.com/questions/15934722/does-servicestack-support-generics-in-end-to-end-typed-requests

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!