How AutoQuery Parameters work when supplied - servicestack

I've been reviewing servicestack and the documentation. In regards to autoquery documentation the pre-autoquery and post auto query design is shown below. Where the DTO does not include the parameter "BookedAfter". It is my understanding in a non-Auto Query scenario that a Get would provide these parameters for the obvious query input options. For Auto Query I have a few questions. First, it would appear to me that if you only provide specific parameters (instead of leaving it wide open) that only those would be allowed for filtering (assuming DTO specific fields)? Is this out of the box or would one need to override the Auto Query implementation? Similarly with below, the code utilized a custom "BookedAfter" parameter. Would one override the implementation, map the more specific parameter wording to a DTO field query scenario? And what would it take to allow additional querying capabilities that came out of the box? I have not been able to find an example from documentation or the community.
[Route("/bookings/search")]
public class SeachBookings : IReturn<SeachBookingsResponse>
{
public DateTime BookedAfter { get; set; }
}
[Route("/bookings/search")]
public class SeachBookings : QueryDb<Booking>
{
public DateTime BookedAfter { get; set; }
}
// Types
public class Booking
{
public int Id { get; set; }
public int ShiftId { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int Limit { get; set; }
}

Auto Query parameters simply match the rules in the configured Implicit Conventions, it's irrelevant if the property is defined on the DTO or not unless you restrict it with EnableUntypedQueries=false in which case it will only look at the conventions from explicit DTO properties.
Your BookedAfter matches the Implicit Convention:
{"%After%", GreaterThanFormat},
You're not limited to the pre-configured conventions and can add/remove your own rules.

Related

ServiceStack Locode Multi select option

I am developing an app and got a feature where an user can assign multiple User's to a Feature . I want the Create Feature page (locode) to populate the list of available users so that the end-user can assign multiple users to a feature. As of now it is not able to populate the User list.
Is there an alternate way or do I need to write the custom template and populate the data on mount() lifecycle?
Below is the DTO,
[Route("/feature", "POST")]
public class CreateFeatureFlag : ICreateDb<Feature>, IReturn<FeatureCreated>
{
[ValidateNotEmpty]
public string Name { get; set; }
[ValidateNotEmpty]
public List<Guid> Users{ get; set; }
}
and the Domain Feature,
[UniqueConstraint(nameof(Name))]
public class Feature : AuditBase
{
[AutoId]
public Guid Id { get; set; }
public string Name { get; set; }
[Reference]
public List<User> Users { get; set; } = new();
}
In Locode it would require a custom Form component to implement it in the same form, but you can add related records by navigating to the child relation then adding the child record where it will preserve and prepopulate the parent id.
This is used a lot in https://talent.locode.dev like navigating to a Job's Job Applications defined by its POCO Reference:
public class Job : AuditBase
{
//...
public List<JobApplication> Applications { get; set; } = new();
}
Which will prepopulate the Job Id reference making it easy to add multiple 1:Many Job Applications.
Checkout its Talent.cs DTOs for more Reference examples.

AutoQuery insight needed

So, I'm working with ServiceStack and love what it offers. We've come to a point where I'm needing to implement a queryable data API... prior to my coming to this project, a half backed OData implementation was done. I'd rather not try and weed through that to make it work.
Which brings me to AutoQuery. I'd like to try it with our SQL Server database. I'm looking at the examples at http://docs.servicestack.net/autoquery-rdbms - but I cannot for the life of me get this to work. Is there something I'm missing here?
I'm using ORMLite to query SQL, and my integration tests I've written show it to be working as I would expect. I have registered the OrmLiteConnectionFactory in the container, as well as my repository which uses it by way of dependency injection.
Specific to code so far, I have a type, and a message that is based on QueryDb:
public class Detail
{
public string Div { get; set; }
public string Reg { get; set; }
}
[Route("/report/detail")]
public class DetailQuery : QueryDb<Detail>
{
public string[] Div { get; set; }
public string[] Reg { get; set; }
}
The message, DetailQuery, is used by my service:
public class ReportService : Service
{
public object Get(DetailQuery dq)
{
// not sure what to put here?
}
}
With all of that, I am able to see the AutoQuery service instance in the admin interface. When I play with the query interface, I hit my service endpoint, and I see the data I expect - filter values in the 'Div' and 'Reg' collections. What am I missing for this to 'just work' here? I have done plenty in ServiceStack accessing my repositories from the Service itself, but I'm trying to gain some insight into what AutoQuery brings to the table here. I have yet to see a 'straight forward' example of how this works... or am I looking for a pot of gold that just isn't there?
AutoQuery works with just the Request DTO i.e. it doesn't need any Service implementation, so your query:
[Route("/report/detail")]
public class DetailQuery : QueryDb<Detail>
{
public string[] Div { get; set; }
public string[] Reg { get; set; }
}
When called from /report/detail will query the Detail RDBMS Table. But your properties here either need to match a column on the Detail table (e.g. Div or Reg) in order to have an exact match (default), however exact matches aren't normally done with arrays they're done with scalar values like a string, e.g:
public string Div { get; set; }
public string Reg { get; set; }
If you're querying a collection you'd be instead making an IN Query where the values would contain list of values, in which case they're normally pluralized:
public string[] Divs { get; set; }
public string[] Regs { get; set; }
and can be called with:
/report/detail?Divs=A,B&Regs=C,D
Which will perform a query similar to:
SELECT * FROM Detail WHERE Div IN ('A','B') AND Rev IN ('C','D')
If that's not the behavior you want it needs to match an implicit convention, e.g:
public string[] DivBetween { get; set; }
Which will then query:
SELECT * FROM Detail WHERE Div BETWEEN 'A' AND 'B'
If you wanted to you could override the AutoQuery service with a custom implementation, e.g:
public class MyQueryServices : Service
{
public IAutoQueryDb AutoQuery { get; set; }
//Override with custom implementation
public object Any(DetailQuery query)
{
var q = AutoQuery.CreateQuery(query, base.Request);
return AutoQuery.Execute(request, q);
}
}
But you'd only need to do that when you want to customize the default behavior, e.g. add an extra filter to the populated SqlExpression.

ServiceStack - Dynamic/Object in DTO

I am running into an issue while looking at SS.
I am writing a custom Stripe implementation and got stuck on web hooks, this in particular:
https://stripe.com/docs/api#event_object
data->object - this can be anything.
Here is my DTO for it:
public class StripeEvent
{
public string id { get; set; }
public StripeEventData data { get; set; }
public string type { get; set; }
}
[DataContract]
public class StripeEventData
{
[DataMember(Name = "object")]
public object _object { get; set; }
}
My hope is to basically just get that object as a string, and then parse it:
var invoice = (StripeInvoice)JsonSerializer.DeserializeFromString<StripeInvoice>(request.data._object.ToString());
Unfortunately the data that is returned from ToString does not have quotes surrounding each json property's name:
Capture
So, the DeserializeFromString returns an object that has everything nulled out.
Why does SS internally strip the quotes out? Is this the proper way to handle a json member that can be one of many different types? I did try the dynamic stuff, but did not have any luck with that either - basically the same result with missing quotes.
I searched very thoroughly for the use of objects and dynamic within DTOs, but there really was nothing that helped with this question.
Thank you!
The issue is that you should never have an object type in DTOs as the serializer has no idea what concrete type to deserialize back into.
The Stripe documentation says object is a hash which you should be able to use a Dictionary to capture, e.g:
public class StripeEventData
{
public Dictionary<string,string> #object { get; set; }
}
Or as an alternative you could use JsonObject which provides a flexible API to access dynamic data.
This will work for flat object structures, but for complex nested object structures you'll need to create Custom Typed DTOs, e.g:
public class StripeEventInvoice
{
public string id { get; set; }
public StripeEventDataInvoice data { get; set; }
public string type { get; set; }
}
public class StripeEventData
{
public StripeInvoice #object { get; set; }
}

C# Asp.net WebApi how to not populate ID field for the Entity when post in CRUD

I am creating simple webapi with CRUD functionality.
I have an entity say for eg: Product.
I am using FluentNHibernate to map the tables. The Id is generated by sequence.
public ProductMap()
{
Table("PRODUCT");
Id(x => x.Id).GeneratedBy.Sequence("SEQ_REC_SCH_INFO").Column("SCH_ID");
Map(x => x.Name, "Name");
Map(x => x.Category, "Category");
Map(x=>x.Price,"Price");
}
So I dont want the Id parameter of the Product to be populated when i post the data.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
}
How to make other fields passed to the api controller except Id field?
Thanks
The best way to pass models between systems is ViewModels, with this approach you can ignore some properties in certain cases and include them in other cases, but if you want to ignore this property in all cases you can use JsonIgnore attribute to prevent these properties from serializing.

Option for Include to only return foreign keys

Does Entity Framework provide an option to retrieve child objects that are only populated with fields that are foreign keys to the parent object?
Sample code might illustrate this better.
Assuming you have the following POCO classes...
public abstract class Base
{
public Guid Id { get; set; }
}
public class User : Base
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Photo : Base
{
public string Description { get; set; }
public User UploadedBy { get; set; }
}
... and assuming you've configured a DbContext correctly, how do you query for a list of all Photos including the UploadedBy object, but where that UploadedBy object only contains the Id property?
I know I can do this...
return await _dbContext.Photos.Include(p => p.UploadedBy).ToListAsync();
... but that returns the entire User object.
I'd like to do something like this...
return await _dbContext.Photos.Include(p => p.UploadedBy.Id).ToListAsync();
... to indicate that I only want the Id property back.
If we could chain those includes we would be able to pick each property on the child object that we want returned.
Or even better, I'd love to be able to configure a setting at a more global level that would make it so that anytime I ask for Photos, give me all members of photos, even child objects, but only populate their foreign keys and nothing more.
The last request is less important though because I could just create the following extension method for each POCO object...
public static IQueryable<Photo> IncludeForigenKeys(this PhotoAlbumDbContext context){
return context.Photos
.Include(photo => photo.UploadedBy.Id);
}
As far as I understand there is no way to partially load a Navigation Property.
However for foreign keys the standard way of accessing these without loading the Nav property is to include the actual key in your model. Eg:
public class Photo : Base
{
public string Description { get; set; }
public int UploadedById { get; set; }
public User UploadedBy { get; set; }
}
This id will be populated even if you don't actually load the whole navigation property.
In the case where you load both you can update either the value on the local or remote end of the nav property and that update will be persisted to the database on save. In my experience EF is very clever around this. The only scenario where it becomes a little more tricky is in unit tests where EF is not maintaining this state.

Resources