I have created a customer service using ServiceStack but i am not able to pass a list of object from this method.
Customer Service -
public class EntityService : Service
{
/// <summary>
/// Request for entity information list
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public object Any(List<CustomerRequest> request)
{
}
}
Request DTO -
[Route("/api/V1/GetCustomerDetails", Verbs = "POST", Notes = "")]
public class CustomerRequest : IReturn<List<CustomerResponse>>
{
[ApiMember(Name = "GetCustomerDetails", Description = "GetCustomerDetails", ParameterType = "body", DataType = "List<BaseRequest>", IsRequired = true)]
List<BaseRequest> _baseRequest {get;set;}
}
public class BaseRequest
{
public string CustId { get; set; }
public string CustName { get; set; }
public string CustAddress { get; set; }
}
Could you please let me know what is the correct way to pass list of object in ServiceStack Post operation.
Each Service in ServiceStack needs to accept a single named concrete Request DTO Type. You can look at the AutoBatched Requests for how to send multiple requests.
E.g, if you want to a Service to accept a List of Types you can inherit List<T>, e.g:
public class CustomerRequests : List<CustomerRequest>{}
public class EntityService : Service
{
public object Any(CustomerRequests request)
{
}
}
Related
When I add a type to AutoQuery, with:
[Route("/templates")]
public class SearchTemplates : QueryDb<Template>
{
public int? Id { get; set; }
public string Name { get; set; }
}
then I can query this object by Id or Name (or whatever other attributes I would add, that the POCO Template has). However it always returns list of items.
It's very useful to be able to GET a single item (not a search result).
This is how I do it:
[Route("/template/{Id}","GET")]
public class SingleTemplate : IReturn<Template>
{
public int Id { get; set; }
}
public Template Get(SingleTemplate request)
{
return Db.LoadSingleById<Template>(request.Id);
}
With all the new AutoQuery and AutoCRUD, it seems to me that the "return a single item by its URL" could also be automatic?
No, All AutoQuery QueryDb<T> services return the same fixed QueryResponse Response DTO as per its Service Contract, i.e:
public abstract class QueryDb<T>
: QueryBase, IQueryDb<T>, IReturn<QueryResponse<T>> { }
public abstract class QueryDb<From, Into>
: QueryBase, IQueryDb<From, Into>, IReturn<QueryResponse<Into>> { }
public class QueryResponse<T> : IQueryResponse
{
public virtual int Offset { get; set; }
public virtual int Total { get; set; }
public virtual List<T> Results { get; set; }
public virtual Dictionary<string, string> Meta { get; set; }
public virtual ResponseStatus ResponseStatus { get; set; }
}
A single result would still populate the Results property, so the JSON wire format would look like:
{ "results":[ {/*Template*/} ] }
You could create your own Custom AutoQuery Implementation that utilizes AutoQuery's IAutoQueryDb API to return your own custom populated DTO but then your Request DTO should NOT inherit from QueryDb<T> as not returning a QueryResponse<T> would break the explicit Service contract of the Request DTO (and all clients expecting it), i.e. you would instead just create a normal ServiceStack Service returning your own custom Response Type.
I have a class of companies and sub companies. These can be nested to any level and displayed in a treeview. I am trying to figure out how to do a self reference in ormlite to build out a hierarchy using the below DTO. With the below I get ambiguous column name errors. Is this approach a bad idea with Service stack?
public class Company : DTOServiceStackBase
{
[AutoIncrement]
[PrimaryKey]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
[References(typeof(Company))]
public int ParentId { get; set; }
}
The below DTO works fine in ORMLite. I would prefer the cleaner implementation above.
public class Company : DTOServiceStackBase
{
[AutoIncrement]
[PrimaryKey]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
[Reference] // Save in SubCompanies table
public List<SubCompany> SubCompanies { get; set; }
}
public class SubCompany : Company
{
[References(typeof(Company))]
public int ChildCompanyId { get; set; }
[References(typeof(Company))]
public int ParentCompanyId { get; set; }
}
EDIT Based on Mythz's Response
Here is my working code for anyone who wants to use it.
[Route("/Company/{Id}", "GET")]
public class GetCompaniesById : IReturn<GetCompaniesFlatTree>
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int? ParentId { get; set; }
}
[Route("/CompaniesFlatTree", "GET")]
public class GetCompaniesFlatTree : IReturn<GetCompaniesFlatTree>
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int? ParentId { get; set; }
}
[Route("/CompaniesTree", "GET")]
public class GetCompaniesTree : IReturn<Company>{}
public class DTOServiceStackBase {
public ResponseStatus ResponseStatus { get; set; } //Automatic exception handling
}
public class Company : DTOServiceStackBase
{
[AutoIncrement]
[PrimaryKey]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
public int? ParentId { get; set; }
[IgnoreDataMember]
public List<Company> SubCompanies { get; set; }
}
[Authenticate]
public class CompanyService : Service
{
/// <summary>
/// Calling SQL directly and casting to the GetCompaniesFlatTree object
/// Don't do this methond of direct SQL unless you cannot do it any other way
/// Why?? Becuase the SQL is not automatically updated when we updated the database schema
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public object Get(GetCompaniesFlatTree request)
{
//This retun uses the DB.Select and works correctly
//return Db.Select<GetCompaniesFlatTree>($"SELECT SC.* FROM Company C Join Company SC ON SC.ParentId = C.Id Where C.ID = {request.Id}");
//This query uses Db.Query due to the BEGIN and CTE Usage
//This does not work with SQL in Memory because it does not support CTE Statements
return Db.Query<GetCompaniesFlatTree>("BEGIN WITH q AS ( SELECT * FROM [Company] WHERE ParentId IS NULL UNION ALL SELECT m.* FROM [Company] m JOIN q ON m.parentId = q.Id) SELECT * FROM q END;");
}
/// <summary>
/// Table Alias is required in this Select due to the self join on company.
/// Table Alisa allows the join to specify which table to return the data from.
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public object Get(GetCompaniesById request)
{
var q = Db.From<Company>(Db.TableAlias("c1"))
.Join<Company>((ChildComp, ParentCompany) =>
ChildComp.Id == ParentCompany.ParentId
&& ParentCompany.Id == request.Id, Db.TableAlias("c2")).Select<Company>(p => new {Id = Sql.TableAlias(p.Id, "c2"), Name = Sql.TableAlias(p.Name, "c2")});
var results = Db.Select<GetCompaniesById>(q);
//See the SQL that was generated
var lastSql = Db.GetLastSql();
return results;
}
/// <summary>
/// Get all Compaines and build the hierarchy
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public object Get(GetCompaniesTree request)
{
//Get all companies
var results = Db.Select<Company>();
//Get Top node
Company topCompany = results.Single(x => x.ParentId == null);
//Find all children
var allChildrenRecursive = GetAllChildrenRecursive(topCompany, results);
return allChildrenRecursive;
}
/// <summary>
/// Populates a Companies collection of child companies
/// </summary>
/// <param name="parent"></param>
/// <param name="results"></param>
/// <returns></returns>
private Company GetAllChildrenRecursive(Company parent, List<Company> results)
{
List<Company> retVal = new List<Company>();
retVal.Add(parent);
//Get Children
var children = results.Where(x => x.ParentId == parent.Id).ToList();
parent.SubCompanies = children;
foreach (var child in children)
{
GetAllChildrenRecursive(child, results);
}
return parent;
}
}
To maintain a tree relationship you would just need to have a nullable int? ParentId in the Company table where Company with NULL ParentId is the root company whilst iterating through the rest of the companies to populate a Dictionary<int,List<Company>> indexed by the parent Id.
This isn't related to an OrmLite Self Reference which just means to maintain the FK Reference to a different table on the table containing the reference.
Good day,
We are experiencing an issue with serialization where a request object set with a value for one property ends up being received by the service with the value assigned to a different property. Please see below for more information.
We are using the 3.9.71 version of ServiceStack NuGet packages. The solution is made up of the following projects:
Project.Host: Used for self-hosting ServiceStack and contains Service classes.
Project.DTO: All services DTOs and surrounding classes.
Project.Tests: Contains unit tests.
The problems has been identified to only one class/service, namely MinimalUser and MinimalUserService, which you can see code for both below:
MinimalUser.cs
namespace Project.DTO
{
[Route("/User/{Identity}", "GET")]
[Route("/User/{Username}", "GET")]
[Route("/User/{DisplayName}", "GET")]
public class MinimalUser : IReturn<MinimalUser>
{
#region Properties
public int? Identity { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string DisplayName { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Language { get; set; }
public string TimeZone { get; set; }
public string Culture { get; set; }
public List<string> Roles { get; set; }
public List<string> Permissions { get; set; }
public DiscUserDetails DiscUserDetails { get; set; }
#endregion
#region Constructors
public MinimalUser() { }
public MinimalUser(UserAuth auth)
{
if (auth != null)
{
this.Identity = auth.Id;
this.Username = auth.UserName;
this.DisplayName = auth.DisplayName;
this.Email = auth.Email;
this.FirstName = auth.FirstName;
this.LastName = auth.LastName;
this.Language = auth.Language;
this.TimeZone = auth.TimeZone;
this.Culture = auth.Culture;
this.Roles = auth.Roles;
this.Permissions = auth.Permissions;
this.DiscUserDetails = auth.Get<DiscUserDetails>();
}
}
#endregion
#region Methods
public static MinimalUser FromUserAuth(UserAuth auth)
{
return auth == null ? new MinimalUser() : new MinimalUser
{
Identity = auth.Id,
Username = auth.UserName,
DisplayName = auth.DisplayName,
Email = auth.Email,
FirstName = auth.FirstName,
LastName = auth.LastName,
Language = auth.Language,
TimeZone = auth.TimeZone,
Culture = auth.Culture,
Roles = auth.Roles,
Permissions = auth.Permissions,
DiscUserDetails = auth.Get<DiscUserDetails>()
};
}
#endregion
}
}
DiscUserDetails.cs
namespace Project.DTO
{
public class DiscUserDetails
{
public int? LocationId { get; set; }
public bool IsActive { get; set; }
public byte NumberOfFailedLoginAttempts { get; set; }
public bool MustChangePasswordAtNextLogon { get; set; }
public int? LastAcceptedPolicyId { get; set; }
}
}
MinimalUserService.cs
namespace Project.Services
{
[Authenticate]
[RequiredRole(new string[] { RoleNames.Admin })]
public class MinimalUserService : Service
{
IUserAuthRepository authRepo = AppHost.Resolve<IUserAuthRepository>() as OrmLiteAuthRepository;
/// <summary>
/// Return a minimalist structure of user insensitive information.
/// </summary>
/// <param name="request">The request containing the ID of the user.</param>
/// <returns>A minimalist structure of user insensitive information.</returns>
public object Get(MinimalUser request)
{
if (request.Identity != null)
return new MinimalUser(authRepo.GetUserAuth(request.Identity.ToString()));
else if (request.Username != null)
return new MinimalUser(authRepo.GetUserAuthByUserName(request.Username));
else
return null;
}
}
}
From my test project, I run the following test:
[TestMethod]
public void GetMinimalUserByUsername()
{
AuthResponse authResponse = client.Post<AuthResponse>("/auth", new Auth
{
UserName = "accountwithadminrole",
Password = "blablabla",
RememberMe = true,
provider = CredentialsAuthProvider.Name
});
MinimalUser request = new MinimalUser
{
DisplayName = BaseAccounts.System,
};
MinimalUser user = client.Get<MinimalUser>(request);
Assert.IsNotNull(user);
}
I clearly see, before issuing the client.Get method, that the request object have all its properties set to null except for the DisplayName which has the value "system". When this request is received by the MinimalUserService Get method, the value "system" is now assigned to the property UserName and DisplayName is null.
Also, I've tried to comment properties one by one in the MinimalUser class, suspecting one of its field could be causing serialization problem and I would end up having random 'Bad Request' when commenting a certain number of properties. Although, I could comment a properties randomly and one property that previously caused a 'Bad Request' would not do it depending on others properties commented out.
I'm really confused about how this can possibly happens. I feel the service and DTO are simple compared to others from this same project but I'm sure I'm doing something really stupid here.
Don't hesitate to ask for more details, it will be my pleasure to give all information you need.
Thank you.
The reason for Username to be populated instead of DisplayName is because of the routes you have defined for MinimalUser. In MinimalUser.cs you defined 2 identical routes:
[Route("/User/{Identity}", "GET")]
[Route("/User/{Username}", "GET")]
[Route("/User/{DisplayName}", "GET")]
Username and Displayname are both strings. This makes it impossible for ServiceStack to determine the appropriate route direct the request to as it cannot differentiate between the routes. You can fix this by either removing a route, or by adding additional text to one of the routes; eg /User/ByDisplayName/{Username}.
ServiceStack self host windows service question, at the link there are two Services: TodoService.cs and HelloService.cs.
I am a little confused, are they different examples or related each other?
//Register REST Paths
[Route("/todos")]
[Route("/todos/{Id}")]
public class Todo //REST Resource DTO
{
public long Id { get; set; }
public string Content { get; set; }
public int Order { get; set; }
public bool Done { get; set; }
}
//Todo REST Service implementation
public class TodoService : RestServiceBase<Todo>
{
public TodoRepository Repository { get; set; } //Injected by IOC
public override object OnGet(Todo request)
{
if (request.Id == default(long))
return Repository.GetAll();
return Repository.GetById(request.Id);
}
//Called for new and update
public override object OnPost(Todo todo)
{
return Repository.Store(todo);
}
public override object OnDelete(Todo request)
{
Repository.DeleteById(request.Id);
return null;
}
}
And
/// <summary>
/// Define your ServiceStack web service request (i.e. the Request DTO).
/// </summary>
[Description("ServiceStack's Hello World web service.")]
[Route("/hello")]
[Route("/hello/{Name*}")]
public class Hello
{
public string Name { get; set; }
}
/// <summary>
/// Define your ServiceStack web service response (i.e. Response DTO).
/// </summary>
public class HelloResponse : IHasResponseStatus
{
public string Result { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
/// <summary>
/// Create your ServiceStack web service implementation.
/// </summary>
public class HelloService : ServiceBase<Hello>
{
protected override object Run(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
They are separate examples of different services you can build with ServiceStack. The ServiceStack examples are made available in a single solution called ServiceStack.Examples, but it contains separate projects.
You are looking in a directory called StarterTemplates.Common, this is simply shared by several of the examples for code reusability. The folder structure does not indicate that TodoService.cs and HelloService.cs are directly related.
The individual projects of the ServiceStack Examples, can be seen here.
Backbone.js TODO app with REST and Redis backend
Creating a Hello World Web service from scratch
I am having a strange issue with ServiceStack (SS). The entity I pass to the method is always serialized to empty json string by SS. So s is always "{}". I debug and see that the entity is a hydrated instance with properties with values.
Any ideas why this is the case?
public virtual void Serialize<TEntity>(TEntity entity, Stream stream)
{
// s is always {}
var s = JsonSerializer.SerializeToString(entity);
// rest is not important at this point...
s = JsvFormatter.Format(s);
using (var writer = new StreamWriter(stream))
{
writer.Write(s);
}
}
I am editing the question show exactly what the passed in (VolumeCreated) entity is.
public class VolumeEvent : IEvent<VolumeID>
{
public VolumeEvent(VolumeID identity)
{
Identity = identity;
}
#region Implementation of IEvent<out VolumeIdentity>
public VolumeID Identity { get; private set; }
#endregion
}
public class VolumeCreated : VolumeEvent
{
public DateTime PublishDate { get; set; }
public string Title { get; set; }
public VolumeCreated(VolumeID identity, string title, DateTime publishDate)
: base(identity)
{
Title = title;
PublishDate = publishDate;
}
}
ServiceStack serializes only serializes public properties.