Make .NET WebApi service deserialize recieved JSON as child object - c#

The system i am developing is using DataContractJsonSerializer.
The service looks like this:
[HttpPost, Route("RunQuery")]
public List<BIResultRecord> RunQuery(BIQuery query) {
// Logic
}
The BIQuery class hierarchy is as follows:
[DataContract]
[KnownType(typeof(BIQuery1))]
public class BIQuery
{
[DataMember]
public string Member { get; set; }
[DataMember]
public QueryTypeEnum QueryType { get; set; }
}
[DataContract]
public class BIQuery1 : BIQuery
{
public BIQuery1()
{
QueryType = QueryTypeEnum.Type1;
}
[DataMember]
public ClassSpecificObject Object { get; set; }
}
THE PROBLEM:
Although i am sending BIQuery1 as a json object it is always deserialized (in my RunQuery method) as the parent object (BIQuery).
WHAT I'VE TRIED:
I've removed the default formatter and added a new one:
public class DataContractJsonFormatter : JsonMediaTypeFormatter
{
public override DataContractJsonSerializer CreateDataContractSerializer(Type type)
{
return new DataContractJsonSerializer(type, new DataContractJsonSerializerSettings() { EmitTypeInformation = EmitTypeInformation.AsNeeded });
}
}
I serialized the object in .NET to see the JSON structure and
cloning it to the request. I saw that it adds
"__type" : "BIQuery1"
to the JSON, which i'm using as well and it changed nothing.
Please help!
MORE DETAILS
The .NET system (client and server) runs in production as is, so only minor changes currently allowed. I'm making also a web client that would work with the existing services over REST.

[HttpPost, Route("RunQuery")]
public List<BIResultRecord> RunQuery(BIQuery query) {
// Logic
}
Your argument in the RunQuery method is the of type BIQuery object. It is deserializing as a BIQuery object because that is the type you are telling the method it will be passed. If you are passing an object of the type BIQuery1 to the method, do this instead:
[HttpPost, Route("RunQuery")]
public List<BIResultRecord> RunQuery(BIQuery1 query) {
// Logic
}

Related

consume JSON from webhook in C#

Hi I'm looking to create a simple webhook receiver and dump the data into a table.
This is for receiving SMS using Zipwhip. Zipwhip will send a post with JSON.
Need to receive the JSON and process.
What is a simple way to accomplish this.
Thanks in advance.
In ServiceStack your callback would just need to match the shape of your Response DTO, e.g:
[Route("/path/to/callback")]
public class CorpNotes
{
public int Departments { get; set; }
public string Note { get; set; }
public DateTime WeekEnding { get; set; }
}
// Example of OrmLite POCO Data Model
public class MyTable {}
public class MyServices : Service
{
public object Any(CorpNotes request)
{
//...
Db.Insert(request.ConvertTo<MyTable>());
}
}
Example uses Auto Mapping Utils to populate your OrmLite POCO datamodel, you may want to do additional processing before saving the data model.
If the callback can send arbitrary JSON Responses in the payload you can use an object property to accept arbitrary JSON however we'd recommend using Typed DTOs wherever possible.
This can be what the receiving method in your controller can look like on the receiving side. Make sure that your receiving and sending json object match.
[HttpPost]
[Route("Edit")]
public JsonResult Edit([FromBody] CorpNotes newMessage)
{return Json(TotalWeekNoteSearch);}
public class CorpNotes
{
public int Departments { get; set; }
public string Note { get; set; }
public DateTime WeekEnding { get; set; }
}
I am actually working on a .net project receiving Json from a Angular front end, so this should be the same concept. Also make sure that what you are receiving is truly a workable object such as.
{Departments: 4, Note: "This is notes 2020Q1W13", WeekEnding: "2020-01-25T00:00:00"}
Also try looking into this example which would be helpful in regards to webhooks.
public class MyWebHookHandler : WebHookHandler
{
public MyWebHookHandler()
{
this.Receiver = "custom";
}
public override Task ExecuteAsync(string generator, WebHookHandlerContext context)
{
CustomNotifications notifications = context.GetDataOrDefault<CustomNotifications>();
foreach (var notification in notifications.Notifications)
{
...
}
return Task.FromResult(true);
}
}
The type of the data is typically JSON or HTML form data, but it is possible to cast to a more specific type if desired.

Dynamically change a type with C#

I am very new to C# and ServiceStack and I am working on a small project that consists on calling a third party API and loading the data I get back from the API into a relational database via ServiceStack's ORMLite.
The idea is to have each endpoint of the API have a reusable model that determines how it should be received in the API's response, and how it should be inserted into the database.
So I have something like the following:
[Route("/api/{ApiEndpoint}", "POST")]
public class ApiRequest : IReturn<ApiResponse>
{
public Int32 OrderId { get; set; }
public DateTime PurchaseDate { get; set; }
public String ApiEndpoint { get; set; }
}
public class ApiResponse
{
public Endpoint1[] Data { get; set; }
public String ErrorCode { get; set; }
public Int32 ErrorNumber { get; set; }
public String ErrorDesc { get; set; }
}
public class Endpoint1
{
[AutoIncrement]
public Int32 Id { get; set; }
[CustomField("DATETIME2(7)")]
public String PurchaseDate { get; set; }
[CustomField("NVARCHAR(50)")]
public String Customer { get; set; }
[CustomField("NVARCHAR(20)")]
public String PhoneNumber { get; set; }
public Int32 Amount { get; set; }
}
My first class represents the API's request with its route, the second class represents the API's response. The API's response is the same for all endpoints, but the only thing that varies is the structure of the Data field that comes back from that endpoint. I've defined the structure of one of my endpoints in my Endpoint1 class, and I am using it in my API's response class. As you can see, I am also defining a few attributes on my Endpoint1 class to help the ORM make better decisions later when inserting the data.
Ok, so the issue is that I have about 15 endpoints and I don't want to create 15 ApiResponse classes when I know the only thing that changes is that first Data field in the class.
So I made something like this:
public class DataModels
{
public Type getModel(String endpoint)
{
Dictionary<String, Type> models = new Dictionary<String, Type>();
models.Add("Endpoint1", typeof(Endpoint1));
// models.Add("Endpoint2", typeof(Endpoint2));
// models.Add("Endpoint3", typeof(Endpoint3));
// and so forth...
return models[endpoint];
}
}
I would like for getModel() to be called when the request is made so that I can pass in the ApiEndpoint field in the ApiRequest class and store the type that I want my Data field to have so that I can dynamically change it in my ApiResponse class.
In addition, there is the ORM part where I iterate over every endpoint and create a different table using the model/type of each endpoint. Something like this:
endpoints.ForEach(
(endpoint) =>
{
db.CreateTableIfNotExists<Endpoint1>();
// inserting data, doing other work etc
}
);
But again, I'd like to be able to call getModel() in here and with that define the model of the specific endpoint I am iterating on.
I've attempted calling getModel() on both places but I always get errors back like cannot use variable as a typeand others... so I am definitely doing something wrong.
Feel free to suggest a different approach to getModel(). This is just what I came up with but I might be ignoring a much simpler approach.
When I DID understand you correctly, you have different API-Calls which all return the same object. The only difference is, that the field "Data" can have different types.
Then you can simply change the type of data to object:
public object Data { get; set; }
And later simply cast this to the required object:
var data1=(Endpoint1[]) response.Data;
You're going to have a very tough time trying to dynamically create .NET types dynamically which requires advanced usage of Reflection.Emit. It's self-defeating trying to dynamically create Request DTOs with ServiceStack since the client and metadata services needs the concrete Types to be able to call the Service with a Typed API.
I can't really follow your example but my initial approach would be whether you can use a single Service (i.e. instead of trying to dynamically create multiple of them). Likewise with OrmLite if the Schema of the POCOs is the same, it sounds like you would be able to flatten your DataModel and use a single database table.
AutoQuery is an example of a feature which dynamically creates Service Implementations from just a concrete Request DTO, which is effectively the minimum Type you need.
So whilst it's highly recommended to have explict DTOs for each Service you can use inheritance to reuse the common properties, e.g:
[Route("/api/{ApiEndpoint}/1", "POST")]
public ApiRequest1 : ApiRequestBase<Endpoint1> {}
[Route("/api/{ApiEndpoint}/2", "POST")]
public ApiRequest2 : ApiRequestBase<Endpoint1> {}
public abstract class ApiRequestBase<T> : IReturn<ApiResponse<T>>
{
public int OrderId { get; set; }
public DateTime PurchaseDate { get; set; }
public string ApiEndpoint { get; set; }
}
And your Services can return the same generic Response DTO:
public class ApiResponse<T>
{
public T[] Data { get; set; }
public String ErrorCode { get; set; }
public Int32 ErrorNumber { get; set; }
public String ErrorDesc { get; set; }
}
I can't really understand the purpose of what you're trying to do so the API design is going to need modifications to suit your use-case.
You're going to have similar issues with OrmLite which is a Typed code-first POCO ORM where you're going to run into friction trying to use dynamic types which don't exist at Runtime where you'll likely have an easier time executing Dynamic SQL since it's far easier to generate a string than a .NET Type.
With that said GenericTableExpressions.cs shows an example of changing the Table Name that OrmLite saves a POCO to at runtime:
const string tableName = "Entity1";
using (var db = OpenDbConnection())
{
db.DropAndCreateTable<GenericEntity>(tableName);
db.Insert(tableName, new GenericEntity { Id = 1, ColumnA = "A" });
var rows = db.Select(tableName, db.From<GenericEntity>()
.Where(x => x.ColumnA == "A"));
Assert.That(rows.Count, Is.EqualTo(1));
db.Update(tableName, new GenericEntity { ColumnA = "B" },
where: q => q.ColumnA == "A");
rows = db.Select(tableName, db.From<GenericEntity>()
.Where(x => x.ColumnA == "B"));
Assert.That(rows.Count, Is.EqualTo(1));
}
Which uses these extension methods:
public static class GenericTableExtensions
{
static object ExecWithAlias<T>(string table, Func<object> fn)
{
var modelDef = typeof(T).GetModelMetadata();
lock (modelDef)
{
var hold = modelDef.Alias;
try
{
modelDef.Alias = table;
return fn();
}
finally
{
modelDef.Alias = hold;
}
}
}
public static void DropAndCreateTable<T>(this IDbConnection db, string table)
{
ExecWithAlias<T>(table, () => {
db.DropAndCreateTable<T>();
return null;
});
}
public static long Insert<T>(this IDbConnection db, string table, T obj, bool selectIdentity = false)
{
return (long)ExecWithAlias<T>(table, () => db.Insert(obj, selectIdentity));
}
public static List<T> Select<T>(this IDbConnection db, string table, SqlExpression<T> expression)
{
return (List<T>)ExecWithAlias<T>(table, () => db.Select(expression));
}
public static int Update<T>(this IDbConnection db, string table, T item, Expression<Func<T, bool>> where)
{
return (int)ExecWithAlias<T>(table, () => db.Update(item, where));
}
}
But it's not an approach I'd take personally, if I absolutely needed (and I'm struggling to think of a valid use-case outside of table-based Multitenancy or sharding) to save the same schema in multiple tables I'd just be using inheritance again, e.g:
public class Table1 : TableBase {}
public class Table2 : TableBase {}
public class Table3 : TableBase {}

Calling the controller method depending on the incoming parameters

I want to implement a certain functionality, but I do not know where to start. I will describe what I have.
Backend
public enum SourceType { Database, Folder }
public class DatabaseSource
{
public string ServerName { get; set; }
public string DatabaseName { get; set; }
}
public class FolderSource
{
public string FolderName { get; set; }
}
public class TestController : ApiController
{
[HttpPost]
[Route("source")]
public void Post([FromBody]DatabaseSource source) //method one
{
}
[HttpPost]
[Route("source")]
public void Post([FromBody]FolderSource source) //method two
{
}
}
Frontend
export enum SourceType {
Database,
Folder
}
export class DatabaseSource {
public ServerName: string;
public DatabaseName: string;
}
export class FolderSource {
public FolderName: string;
}
var source = new DatabaseSource();
source.ServerName = "serverName";
source.DatabaseName = "dbName";
var obj = {
sourceType: SourceType.Database,
source: source
};
Now imagine that I will send obj to the server. I want that specific controller method to be called depending on the enum. How can I do this?
P.S. The example is greatly simplified.
Your implementation is inconsistent for what you've specified in code.
On the front-end you are describing an object which has a sourceType field and a source object property, while on the backend you're overloading the ApiController method and mapping different REST object resources to a single HTTP method and endpoint (which I believe will not work).
There is no magic way for the ApiController to use your enum property to differentiate between the object types automatically.
A simpler (and better) implementation would be to have separate ApiController classes for your Database and Folder source object POST calls. This follows the principle of REST API design where you are essentially mapping basic CRUD operations to the HTTP methods with object types.
If your intention is to perform an operation based on these parameter objects, then clarify the intention via the API routing for the endpoint as below:
public class TestController : ApiController
{
[HttpPost]
[Route("ETLLoad/Database/source")]
public void Post([FromBody]DatabaseSource source) //method one
{
}
[HttpPost]
[Route("ETLLoad/Folder/source")]
public void Post([FromBody]FolderSource source) //method two
{
}
}

How to pass any type of object as input parameter with only 1 method

I've a WCF service with one method which will be called from multiple web API controllers like in the below code.
public string Print(PdfPrinterRequest _request)
{
PdfPrinterService.PdfPrinterClient _Client = new PdfPrinterService.PdfPrinterClient();
PdfPrinterResponse _response = new PdfPrinterResponse();
return _Client.Print(_request.Document, out _pdfResponse);
}
PdfPrinterRequest(Document class) is the entity which I'm passing to get the response message from WCF service.Currently the document class holds few properties(REquest Header). I would like to call the same Print method from other API and pass type 'Customer' to WCF service. How can i achieve this? Can anyone please suggest me the correct implementation?
Below is my WCF service code,
public class PdfPrinterService : IPdfPrinter
{
public PdfPrinterResponse Print(PdfPrinterRequest request)
{
return PdfPrinterFacade.PrintPdf(request);
}
}
public static PdfPrinterResponse PrintPdf(PdfPrinterRequest request)
{
PdfPrinterResponse response = new PdfPrinterResponse();
//Process the request and send back the response message
}
[MessageContract]
public class PdfPrinterRequest
{
private Document _document;
[MessageBodyMember]
public Document Document
{
get { return _document; }
set { _document = value; }
}
}
How to pass a dynamic class object as a parameter in place of PdfPrinterRequest which is no bound to only one type(Document)? Please suggest.
Thanks,
If this service does not need to be interoperable, you can switch to NetDataContractSerializer, which uses full .NET type information, and is able to serialize many more types (but not any type - that's impossible).
Grab the UseNetDataContractSerializerAttribute from this answer and apply like so:
[UseNetDataContractSerializer]
public class PdfPrinterService : IPdfPrinter
{
public PdfPrinterResponse Print(PdfPrinterRequest request)
{
return PdfPrinterFacade.PrintPdf(request);
}
}
[MessageContract]
public class PdfPrinterRequest
{
[MessageBodyMember]
public object Document { get; set; }
}

ODataConventionModelBuilder with inherited entities

I've an WebAPI OData v3 interface using ODataConventionModelBuilder. It contains some entities which are inherited, and also a model which has a collection of the abstract object:
public abstract class BaseObject
{
[Key]
public int Id { get; set; }
[ForeignKey("Object3")]
public int? ParentId { get; set; }
public virtual Object3 Parent { get; set; }
}
public class Object1: BaseObject
{
}
public class Object2: BaseObject
{
}
public class Object3
{
[Key]
public int Id { get; set; }
public ICollection<BaseObject> MyObjects { get; set; }
}
I'm calling the interface using Breeze with client side metadata, using expand:
http://example.com/api/Object3?$expand=MyObjects
The server response looks like this:
{
"odata.type":"MyNamespace.Object1",
"odata.id":"http://example.com/api/BaseObject(1)",
"Parent#odata.navigationLinkUrl":"http://example.com/api/BaseObject(1)/Parent",
"Id":1,
"ParentId":1
}
Breeze now recognizes this as an entity of type Object1. But if I modify the entity and save the changes it makes a POST request to http://example.com/api/BaseObject(1). To being able to handle the different concrete types I need the POST request to go to the specific controller http://example.com/api/Object(1).
What do I need to change so that Breeze makes to update POST calls to the concrete controller and not the controller of the base object?
UPDATE: After inspecting the Breeze source code, it seems like Breeze uses the odata.id as URI for the POST request. Is it somehow possible to have the OData API return the URI for the concrete object as odata.id instead of the base object?
I got this working with a nasty hack by removing extraMetadata from all entities before saving with breeze:
var entities = manager.getEntities(null, breeze.EntityState.Modified);
for (var i = 0; i < entities.length; i++) {
delete entities[i].entityAspect.extraMetadata;
}
It there are no extraMetadata (which contains the odata.id) are available, breeze calculates the URI to the controller of the concrete model.
I don't know if there's a better solution available, that the OData API sends the correct odata.id in the first place.

Categories