Why does Odata can't parse JSON when using Extend? - odata

I'm using OData v4 in my project Web Api (.Net Core 3.1). I'm using Devart Linqconnect to generate my model.
When I want to use "Extend" I'm getting message "can't parse JSON. Raw value".
"Select" and "Filter" working good. I'm wondering why "Extend" not working properly.
[EnableQuery(PageSize = 2)]
[HttpGet]
public IActionResult Get(ODataQueryOptions<Tasks_Object> query)
{
var items = query.ApplyTo(DBContext.Tasks_Objects.Where(i => i.Delete== null));
return Ok(items);
}
configuration of my controller/action in EDMModel.
var tasks_object = builder.EntitySet<Tasks_Object>("Task").EntityType.HasKey(e => e.ZAD_ID);
var task = builder.EntityType<Task>().HasKey(a=>a.ZAD_ID);

The problem is solved.
"Extend" not working with LinqConnect because LQ using property EntitySet<> as a relation to other table, but we can change it.
There isn't problem with EF Core because EF Core using properties as a List<> not EntityRef.

Related

Accessing querystring in ASP.NET MVC6

I am trying to access query string parameters in my ASP.NET MVC6 applications. But it seems unlike MVC5 and web forms, QueryString doesn't have any indexer and I can't say something like:
string s = Request.QueryString["key1"] //gives error
So, my question is - how do I access query string parameters in MVC6?
Surprisingly Request.Forms collection works as expected (as in MVC5 or web forms).
Thank you.
Getting query with an indexer is supported.
See MVC code test here - https://github.com/aspnet/Mvc/blob/e0b8532735997c439e11fff68dd342d5af59f05f/test/WebSites/ControllersFromServicesClassLibrary/QueryValueService.cs
context.Request.Query["value"];
Also note that in MVC 6 you can model bind directly from query by using the [FromQuery] attribute.
public IActionResult ActionMethod([FromQuery]string key1)
{
...
}
So, my question is - how do I access query string parameters in MVC6?
You can use Request.Query which is new addition in ASPNET 5.
var queryStrings = Request.Query;
The URL I am going to try was - http://localhost:12048/Home/Index?p=123&q=456
And you can get All Keys using -
queryStrings.Keys
And then you can get the values by iterating keys -
var qsList = new List<string>();
foreach(var key in queryStrings.Keys)
{
qsList.Add(queryStrings[key]);
}

Does breeze's withParameters method work with an OData service adapter?

I'm using OData with Entity Framework to select some records from a database. The records each have a path to a file with text content. In a single service call I'd like to be able to filter the DB records as well as filter the resulting objects based on the content of the files that the records point to. Because I can't mix LINQ to EF with LINQ to Objects, I believe the easiest way to accomplish this is to add an additional query parameter to the standard OData parameters that defines how to filter for file content after the standard odata filters have been applied.
Is looks like the entity query's "withParameters" method is the way to add a non-standard parameter but it doesn't seem to work with version 1.4.9 of breeze.
Am I doing something wrong or is there any intention to make this method work for the OData service provider?
As a workaround to this shortcoming, I've found that you can declare your entity to query with the parameters as part of the entity name, like so:
var entityId = 4;
var answerId = 6;
var entityToQuery = "MyEntity(EntityId=" + entityId + ",answerId=" + answerId + ")";
Then, build your breeze query:
var query = breeze.EntityQuery.from(entityToQuery);
This would map to an OData endpoint such as:
public IQueryable<MyEntity> GetMyEntity([FromODataUri] int entityId, [FromODataUri] int answerId)
{
}
No, you need to use the WebApi adapter. This is not a breeze shortcoming, it's a OData shortcoming because OData doesn't support this syntax.
However, the WebApi adapter does do everything you want and this is the Breeze default. Please see the docs for more information.

.NET Web API issues with deserializing JSON to object

I overtook a project which was developed by a company for us, unfortunately we do not get much support and in the long term, we should accomplish maintainence by ourselves for it. The application consists of a simple Web client (HTTP, JavaScript, Knockout Framework) and a REST API Service (.NET 4.5, ASP.NET MVC I guess).
Currently I am only modifiying the client, so the Server should still work as expected.
On the clientside I modified the Knockout View Model a little bit (added some computables and optimized presentation of some values). The View Model consists of 'Issues' and 'Comments' (as an array on issues, its an Bug tracker in fact). If I create a new issue, description is added to a first comment, the whole Model is JSON.stringified and the send to the .NET API Service. I prooved with Firebug, that the JSON that gets posted looks like this:
{
"Id":0,
"Title":"THis is a title",
"Comments":[
{
"Id":1,
"Text":"this is the first comment"
}
]
}
On the client side, I have a "IssueController":
[HttpPost]
public HttpResponseMessage PostIssues( Issue issue ) {
//issue should already hold the deserialized content of the JSON here,
//but it only contains 'Id' and 'Title' not the 'Comments' (empty List)
...
}
The issue domain model object also has an array for holding comments, but on this end its empty already. The .NET code doesn't have any part which explicitely parses the JSON, as far as I understood it, the MVC Framework does this implicitely by equal property names (Is that right?).
The deserialization already worked as expected, so the .NET Code should be fine, but I looks like that I have modified the JSON in a way, that this implicit mapping of comments does not work anymore. Unfortunately I dont have much experiences with the .NET MVC Framework (or is it just the .NET WebAPI Framework, cannot even tell you that).
These are my questions:
What kind of .NET REST API Framework is that? How can I distinguish?
How is this implicit JSON deserialization working, and what are its pitfalls, for example when portions of the JSON doesn't get deserialized as expected? Especially on the client side (as I said, I didn't modify the server code)
Any ideas about what I could have done to the JSON, that the server doesn't like it anymore? ;)
EDIT
Issue.cs looks like this (simplified for sure):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Bo;
using Projects.DomainObjects;
namespace Projects.Models {
public class Issue : DomainObject {
public Issue() {
this.Comments = new List<Comment>();
}
public long Id { get; set; }
private string _Title;
public string Title { get { return _Title; } set { _Title = value; NotifyChanged(); } }
public List<Comment> Comments { get; set; }
}
Comment.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Common;
using Projects.DomainObjects;
namespace Projects.Models {
public class Comment : DomainObject {
public Comment() {
}
public long Id { get; set; }
public string Text { get; set; }
}
I just tried to your code straight from your post and it worked for me.
So there are a few things that may be going wrong on your side:
When you post the object to the server, make sure you are converting back from Knockout observable to a json object. So in your ajax request, make sure it looks like: data: ko.toJSON(issue) and not just data: issue.
When you post the object to the server make sure you sent header content-type: application/json
Here are the answers to your other questions:
What kind of .NET REST API Framework is that? How can I distinguish?
This doesn't look like anything custom (at least, what you posted) it is just straight Web API in .NET.
How is this implicit JSON deserialization working, and what are its pitfalls, for example when portions of the JSON doesn't get deserialized as expected? Especially on the client side (as I said, I didn't modify the server code)
Deserialization on the server uses the collection of formatters that work based on the content-type set by the client. This can get complex if you want to customize it but there is information here
Any ideas about what I could have done to the JSON, that the server doesn't like it anymore? ;)
As I said your code worked for me!
Solved it, the Problem was a computed that returns a sorted array of the comments. So my knockout model contains an
self.Comments = ko.observableArray([]); //...which gets filled with the comments of an issue
and a
self.CommentsSorted = ko.computed(function() {...}); //...which simply returns a sorted Comments array
So when I serialize this model, the posted JSON now represents the Comments Array, but also the CommentsSorted Array. Only when I do
var i = ko.toJS(issue);
delete i.CommentsSorted;
before I post i as data, .NET is able to deserialize Comments correctly.
The mysterious thing about this is, that there were always other computed fields in my knockout model, which get ignored completely by .NET and do not disturb deserialization in any way. So it seems that it depends mainly on the name and type of the fields in the model (perhaps if the first letters are equal?).
The good thing: it works now
The bad thing: it isn't really deterministic how .NET does the deserialization of JSON data and I am also not able to debug if it doesn't behave as expected.

Make JSON.NET and Serializable attribute work together

I'm using JSON.NET and had some troubles in the past during WEBAPI objects deserialization. After doing some research I've found that the class was marked with [Serializable]. When I removed this the deserialization was just fine.
More detailed information about this can be found here:
Why won't Web API deserialize this but JSON.Net will?
Now it comes to the problem that I use binaryformatter to create a hash value calculated from this object class.
But Binaryformatter requires that the class must be marked as [Serializable].
Could you recommend me any approach to make both things work at the same time?
Found the solution:
First, check that your Newtonsoft.JSON version is greater than 4.5 or just update with NuGET
According to the version notes, both can work together starting from this version using some extra annotations.
http://james.newtonking.com/archive/2012/04/11/json-net-4-5-release-2-serializable-support-and-bug-fixes
"Now if you are serializing types that have the attribute and don’t want the new behaviour, it can either be overridden on a type using the JsonObjectAttribute"
[JsonObject]
[Serializable]
public class Foobar {
Now it is possible to use JSON.NET and, in my case, the binaryformatter with the [Serializable] attribute.
An alternative to specifying JsonObject on each class is to tell web.api to ignore Serialize attributes globally. This can be done by resetting the DefaultContractResolver on the web api JsonFormatter:
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DefaultContractResolver();
(using NewtonSoft.Json.Serialization where config is the System.Web.Http.HttpConfiguration)
As of NewtonSoft v4.5 the IgnoreSerializableAttribute property on the DefaultContractResolver is set to true but the web api wrapper, around DefaultContractResolver, has this set to false by default.
I was using a POCO with Serializable attribute. In the first case while Posting Request to a WebApi worked by using the following method:
JsonMediaTypeFormatter f = new JsonMediaTypeFormatter()
{
SerializerSettings = new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver()
{
IgnoreSerializableAttribute = true
}
}
};
var result = client.PostAsJsonAsync<IEnumerable<Company>>("company/savecompanies", companies).Result;
//I have truncated the below class for demo purpose
[Serializable]
public class Company
{
public string CompanyName {get;set;}
}
However, when I tried to read the response from WebApi (Which was posted back as JSON), the object was not properly deserialized. There was not error, but property values were null. The below code did not work:
var readObject = result.Content.ReadAsAsync<IEnumerable<Company>>().Result;
I read the documentation as given on Newtonsoft.Json website https://www.newtonsoft.com/json/help/html/SerializationAttributes.htm and found the following and I quote from that site:
Json.NET attributes take precedence over standard .NET serialization
attributes (e.g. if both JsonPropertyAttribute and DataMemberAttribute
are present on a property and both customize the name, the name from
JsonPropertyAttribute will be used).
So, it was clear if Newtonsoft.Json attributes are present before the standard .NET attributes they will take precedence. Hence I could use the same class for two purposes. One, when I want to post to a WebApi, Newtonsoft Json serializer will kick in and Two, when I want to use BinaryFormatter.Serialize() method std .NET Serializable attribute will work.
The same was confirmed with the answer given above by #Javier.
So I modified the Company Class as under:
[JsonObject]
[Serializable]
public class Company
{
public string CompanyName {get;set;}
}
I was able to use the same class for both purposes. And there was no need for the below code:
JsonMediaTypeFormatter f = new JsonMediaTypeFormatter()
{
SerializerSettings = new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver()
{
IgnoreSerializableAttribute = true
}
}
};

Implementing $select with WebApi and ODataQueryOptions

I'm trying to implement some OData functionaility with a custom DAL using ODataQueryOptions.
My DAL uses design time generated typed data tables. By intercepting the SelectExpand property of ODataQueryOptions i can get our DAL to only load the columns required.
How do i then return just the data required.
I am currently tipping the data from our type datatables into a ListOf some typed data tranfer objects but then end up with lots of null data from the columns which aren't required.
I feel like i should be able do some LINQ query to select the just the columns I need straight from the typed datatable bypassing using typed DTOs altogether. Is this possible?
You need to do the same thing that SelectExpandQueryOption.ApplyTo does.
1) Optimize the query to the backend. Instead of getting the whole entity from the database, get only the properties the client asked for and wrap that result in an IEdmEntityObject. Return the collection as EdmEntityObjectCollection. This step is optional. You could choose to ignore this step and return IQueryable and still get $select to work.
2) Tell the OData formatter to serialize only the requested fields. This can be done by stuffing the SelectExpandClause on the Request object using the extension method Request.SetSelectExpandClause.
public class CustomersController : ODataController
{
public IEnumerable<Customer> Get(ODataQueryOptions<Customer> query)
{
Customer[] customers = new[] { new Customer { ID = 42, Name = "Raghu" } };
// Apply query
var result = customers;
// set the SelectExpandClause on the request to hint the odata formatter to
// select/expand only the fields mentioned in the SelectExpandClause.
if (query.SelectExpand != null)
{
Request.SetSelectExpandClause(query.SelectExpand.SelectExpandClause);
}
return result;
}
}

Resources