How can I translate an href into a RequestDto using ServiceStack? - rest

I'm building a ReST API that supports linked resource expansion, and I can't work out how to use ServiceStack's native binding capabilities to translate a URL into a populated 'request DTO' object.
For example, say my API allowed you to retrieve information about a band using this request:
GET /bands/123
< 200 OK
< Content-Type: application/json
{
"href": "/bands/123",
"name": "Van Halen",
"genre": "Rock",
"albums" {
"href" : "/bands/1/albums",
}
}
If you wanted to expand the band's album list, you could do this:
GET /bands/1?expand=albums
< 200 OK
< Content-Type: application/json
{
"href": "/bands/123",
"name": "Van Halen",
"genre": "Rock",
"albums" {
"href" : "/bands/1/albums",
"items": [
{ "href" : "/bands/1/albums/17892" },
{ "href" : "/bands/1/albums/28971" }
]
}
}
I'm using ServiceStack, and I'd like to perform this inline expansion by re-using existing service methods.
My ServiceStack response DTOs look like this:
public class BandDto {
public string Href { get; set; }
public string Name { get; set; }
public AlbumListDto Albums { get; set; }
}
public class AlbumListDto {
public string Href { get; set; }
public IList<AlbumDto> Items { get; set;}
}
public class AlbumDto {
public string Href { get; set; }
public string Name { get; set; }
public int ReleaseYear { get; set; }
}
My ServiceStack request/route objects are like this:
[Route("/bands/{BandId}", "GET")]
public class Band : IReturn<BandDto> {
public string Expand { get; set; }
public int BandId { get; set; }
}
[Route("/bands/{BandId}/albums", "GET")]
public class BandAlbums : IReturn<AlbumListDto> {
public int BandId { get; set; }
}
and the actual services that handle the requests are like this:
public class BandAlbumService : Service {
public object Get(BandAlbums request) {
return(musicDb.GetAlbumsByBand(request.BandId));
}
}
public class BandService : Service {
private IMusicDatabase db;
private BandAlbumService bandAlbumService;
public BandService(IMusicDatabase musicDb, BandAlbumService bandAlbumService) {
this.db = musicDb;
this.bandAlbumService = bandAlbumService;
}
public object Get(Band request) {
var result = musicDb.GetBand(request.BandId);
if (request.Expand.Contains("albums")) {
// OK, I already have the string /bands/123/albums
// How do I translate this into a BandAlbums object
// so I can just invoke BandAlbumService.Get(albums)
var albumsRequest = Translate(result.Albums.Href);
result.Albums = bandAlbumService.Get(albumsRequest);
}
}
In the example above, say I have calculated the string /bands/123/albums as the HREF of Van Halen's album list.
How can I now use ServiceStack's built-in binding capabilities to translate the string /bands/123/albums into a BandAlbums 'request' object that I can pass directly into the BandAlbumService, get back a populated BandAlbumsDto object and include it in my response object?
(and yes, I'm aware this probably isn't an optimal approach in terms of minimising database hits. I'm going to worry about that later.)

RestPath should be able to help you:
I think this should work:
var restPath = EndpointHostConfig.Instance.Metadata.Routes.RestPaths.Single(x => x.RequestType == typeof(AlbumRequest));
var request = restPath.CreateRequest("/bands/123/albums")

Related

EntityFramework Many-to-Many serilization cycle

I have a code first Blazor WebAssembly application in which I have a Many-to-Many relationship.
public class A
{
public A()
{
this.Bs = new HashSet<B>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<B> Bs { get; set; }
}
public class B
{
public B()
{
this.As= new HashSet<A>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<A> As { get; set; }
}
In my client, I call the Get method of the server AController. I would like to have in each A object, the Bs ICollection.
If the Get method is like this, the Bs collection is null :
[HttpGet]
public IEnumerable<A> Get()
{
return _context.A.ToList();
}
If it is like this, to inlude Bs, I have an exception ("System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles.")
[HttpGet]
public IEnumerable<A> Get()
{
return _context.A.Include(a => a.Bs).ToList();
}
So in my Startup.cs (on the server) I had the following in the ConfigureServices method
services.AddControllers().AddJsonOptions(o =>
o.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve);
So now, the serialization works, but the deserialization fails because the JSON is different, not only a List of A.
In the client, I call the get method of the AController like this :
var response = await _httpClient.GetAsync("my_api_adresse");
return await response.Content.ReadFromJsonAsync<List<A>>();
But du to the ReferenceHandler.Preserve the JSON is like this, so the desirialization can't work and raises an exception :
{
"$id": "1",
"$values": [
{
"$id": "2",
"id": 4,
"name": "nameA3",
"Bs": {
"$id": "3",
"$values": [
{
"$id": "4",
"id": 1,
"Name": "NameB1",
"As": {
"$id": "5",
"$values": [
{
"$ref": "2"
}
]
}
}
]
}
}
]
}
What could I do to be able to include Bs collection into A objects and be able to serialize and deserialize the response without any trouble ?
Try using DTO objects (data transfer objects).
There are many ways. One is:
Create two new classes that are similar to class A and class B, but this one without a list of A objects. Instead, use a list of A Ids. That would solve your problem.
Comment gere if you need code sample

Returning ObjectID as a string from ASP.NET Core

How to you get a string representation of the ObjectId returned via ASP.NET Core.
I have the following result of an action in my controller:
return new ObjectResult(new { session, user });
One of the user properties is the UserId that is of the ObjectId type.
However, this gets returned in the response as
"id": {
"timestamp": 1482840000,
"machine": 6645569,
"pid": 19448,
"increment": 5052063,
"creationTime": "2016-12-27T12:00:00Z"
}
I would like the response to simply be 58625d5201c4f202609fc5f3 that is the string representation of the same structure.
Are there any easy way to do this for all returned ObjectIds?
EDIT
Adding some more data
Here are the user class. ObjectId is MongoDB.Bson.ObjectId
public class User
{
public ObjectId Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
public string PasswordSalt { get; set; }
}
The get method in my controller. Controller is Microsoft.AspNetCore.Mvc.Controller.
[HttpGet("{id}", Name = "GetUser")]
public async Task<IActionResult> Get(ObjectId id)
{
var user = await _repository.GetOne<User>(id);
if (user == null) return NotFound();
return new ObjectResult(user);
}
And this is the method from my repository:
public async Task<T> GetOne<T>(ObjectId id)
{
var collectionname = typeof(T).Name;
var collection = _database.GetCollection<T>(collectionname);
var filter = Builders<T>.Filter.Eq("_id", id);
var result = await collection.Find(filter).ToListAsync();
return result.FirstOrDefault();
}
You have to explicitly write a ObjectID to JSON convertor. Please check the link below:
https://stackoverflow.com/a/37966098/887976

How to access class properties using interface object which are not defined in the interface?

I am new to this forum. Now-days I am studying design patterns. I learnt about factory pattern and implemented simple example as shown below.
public interface IPlc
{
string Name { get; set; }
void Testping();
}
Public AbbPlc : IPlc
{
string Name { get; set; }
string slotnumber { get; set; }// Property relevant with this class
public void Testping()
{
console.writeline("ping executed successfully");
}
}
Public SiemensPlc : IPlc
{
string Name { get; set; }
string Racknumber { get; set; }// Property relevant with this class
public void Testping()
{
console.writeline("ping executed successfully");
}
}
//In Main Program
var objAbb = new IPlc() //Created object from interface
objAbb.Name = "46IC-PLC"; //Works OK
objAbb.slotnumber "3"; //Not works
var objSiemens = new IPlc() //Created object from interface
objSiemens.Name = "45IC-PLC"; //Works OK
objSiemens.Racknumber "5"; //Not works
Could you please tell me why these two methods doesn't work?
What I have to do to access properties from Interface object?
Do I need to use one more pattern with factory like decorator?

Web API OData $expand doesn't return complex types

Domain model:
public class Course
{
public int CourseId { get; set; }
public virtual ICollection<TeeSet> TeeSets { get; set; }
}
public class TeeSet
{
public int TeeSetId { get; set; }
public int CourseId { get; set; }
public CourseRating MensRating { get; set; }
}
The following query does not include the CourseRating complex type when Courses are expanded to include TeeSets.
GET /api/courses?$expand=TeeSets
public class CoursesController : ApiController
{
[Queryable]
public IQueryable<Course> Get()
{
return _uow.Courses.GetAll();
}
}
The JSON serialized result does not include the MensRating complex type (CourseRating):
[
{
"teeSets": [
{
"teeSetId": 1,
"courseId": 7
},
{
"teeSetId": 2,
"courseId": 7
}
],
"courseId": 7,
}
]
However, a quick test against the DbContext returns the CourseRating complex type on TeeSets like I would expect:
[TestMethod]
public void Get_Course_With_TeeSets()
{
using (CoursesContext ctx = new CoursesContext())
{
var courses = ctx.Courses.Where(x => x.CourseId == 7).Include(x => x.TeeSets).FirstOrDefault();
}
}
Entity Framework 6 and Web API 2 used.
You should expand MensRating as well like this, GET /api/courses?$expand=TeeSets/MensRating
When we build an implicit EDM model for supporting QueryableAttribute with ApiController, we treat every type as an entity type to get around the OData V3 limitation that complex types cannot refer to entity types. And, this means that you have to expand explicitly every non primitive type.
Add AutoExpand on MensRating Property can make this work.

Using Generic Type DTO In ServiceStack Request

I was wondering if it's possible to use a Templated DTO requested of the type public class
RequestDTO<T, U> where T : class where U : class
{
public T ContextRequest { get; set; }
public U Request { get; set; }
}
And pass it to my service's request...
I've tried a simple example of type
public class MyContext
{
//removed some fields
public int IdUser {get;set;}
}
public class MyType1Request
{
public int IdRequest {get;set;}
}
public class MyType2Request
{
public DateTime SomeReallyImportantDate { get; set; }
}
public class UtenteService : Service
{
public UtenteService()
{
}
public string Any(RequestDTO<MyContext,MyType1Request> request)
{
return string.Format("User:{0} - IDRequest: {1}", request.ContextRequest.IdUser, request.Request.IDRequest);
}
public object Any(RequestDTO<MyContext, MyType2Request> request)
{
return string.Format("Utente:{0} - Data : {1}", request.ContextRequest.IDUtente, request.Request.DataLavorativa);
}
But I got this as far I try to access to the service
Sequence contains more than one matching element
Another solution is to implement in each my DTO Request object a IContext (and maybe IContextExt IContextSpecificManner) into the request object...but I would prefer to use the first approach
Thanks
AFAIK - This should work. The only thing that I am missing here is, you need to register route in apphost like:
Routes.Add>("/Utente/{IdUser}/{IdRequest}")