Get BreezeJS ODATA query parameters on server side to update entity during query - breeze

How do I get handle of the OData query parameters in a Brezze WebApi controller?
I have a endpoint where only a single entity will be queried at a time. The controller just calls a repository that returns a context.Set().AsQueryable();
I want to be able to get the entity ID out of the parameters and update(plain EF) a count property on it before querying it for the response.
Thanks

I found what I needed.
By adding a ODataQueryOptions<T> odataQueryOptions argument to the endpoint I was able to access the OData query parameters and get the ID I needed by getting this property:
var where = odataQueryOptions.RawValues.Filter;
Filter value in my case looks like this: Id eq 'dd3d6cb2-bc7a-467e-9730-c43c333b6fda'
Edit:
If you use .withParameters() on your breeze query object, you can pass additional parameters to your end-point without bothering to deal with OData.

Related

Recreate ODataQueryOptions from Request URL

In my ASP.NET Core 3.1 project
I have a RabbitMQ consumer setup to fetch Database records. I am passing the OData based request URL with $expand, $filter, $count, $select as AllowedQueryOptions. I want to create an ODataQueryOptions object using just the Request URL and apply it to the DBSet I get from EF Core.
I am not able to achieve this outside of an OData Controller. How can I reuse the Query options for the same EDM model?

SAPUI5 OData model with different metadata URI

I am using an OData model for a table, but the data is not loaded. The OData model is created by constructor in the following way:
new sap.ui.model.odata.ODataModel("/XMII/IlluminatorOData/QueryTemplate?QueryTemplate=testTemplate", {annotationURI: "/XMII/IlluminatorOData/$metadata"});
I cannot use the root URI of the OData service, because I need to specify a QueryTemplate.
After setting the model to the table, bindRows method is invoked with parameter "/Row".
table.bindRows("/Row")
The template for a column is a TextField.
template: new sap.ui.commons.TextField().bindProperty("value", "PROJECT")
The data is not shown and not even requested. Only metadata is fetched. What am I doing wrong with OData model? If I fetch the URI which was passed to the model with a get request, content is available.
Are you sure the URL is right?
Shouldn't it be something like "/XMII/IlluminatorOData/?QueryTemplate=testTemplate"
The ODataModel needs to load the metadata in order to work, this means it fetches it from "[ServiceUrl]$metadata", which would be "/XMII/IlluminatorOData/QueryTemplate/$metadata" in your case. But you also use the metadata as annotation source - there you use a different URL, so I guess you first one is wrong...?

fetchEntityByKey Queries against an invalid api route

My entity name is "Products" but in my WebApi the route is defined as "GetProducts" so in default it will query against /api/Products? instead of /api/GetProducts, is there any way to specify where it should fetch the data from the server if it can't find what i want in the cache.
So far i got this
manager.fetchEntityByKey("Products", productId, true)
.then(fetchSucceeded)
.fail(queryFailed);
This will call http://localhost:1990/breeze/Products?$filter=Id%20eq%201
But i want it to call http://localhost:1990/breeze/GetProducts?$filter=Id%20eq%201 instead
There's also another option to specify this on the server. With WebApi 2, you can use the RouteAttribute on your controller action to customize the routing.
See http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
EDIT:
On the client, the Breeze EntityType has a property called defaultResourceName which you can modify as well to tell Breeze which default resource name to use if only an entity type name is specified.
var productType = metadataStore.getEntityType('Products');
productType.defaultResourceName = 'GetProducts';
//Will create fetch request to breeze/GetProducts
manager.fetchEntityByKey('Products', productId);
You can change entity's default resourceName(that hits the breeze webapi method name) with
manager.metadataStore.setEntityTypeForResourceName("GetProducts", "Product")
But be sure the code above is executed when promise of manager.fetchMetadata() is resolved:
manager.fetchMetadata().then(function(){
manager.metadataStore.setEntityTypeForResourceName(...);
})
If setEntityTypeForResourceName is called before metadata has loaded you will get an error, i.e
Unable to locate 'entityTypeName' of: Product
You can set the Resource for the given entity type which would hit the resource instead -
manager.metadataStore.setEntityTypeForResourceName('GetProducts', 'Products');
But as a note it seems like a design flaw to name your entity Products instead of Product.

Calling a web api method with custom object

Usually I send my post request with a custom parameter and a custom return and object using
HttpClientExtension.PostAsJsonAsync<T>
This allows my to call a post method with a custom object.
Now, I want to be able to send my custom object as a parameter and return value to a GET Method.
Lets say my method signature is
[HttpGet]
public MyMethodResponse MyMethod(MyMethodRequest request)
How can I send a request when I have an instance of MyMethodRequest ?
Thanks.
You need to encode MyMethodRequest onto the query string. You can either encode it as separate query string parameters or as a single one. You have handle the encoding yourself on the client side, remembering to URI-encode the parameters. Decoding is done using a custom ModelBinder or TypeConverter respectively. This article shows examples of binding a complex object on the query string.

OData POST entity creation with expand

I'm attempting to create an instance of an entity and two child entities at the same time.
If I post the following JSON to the /user_objects resource it happily creates the parent user_object entity and the linked User_object_attribute entity. The only problem is that I can't seem to expand the result set to include the new user_object_attribute so I effectively have the most uptodate version of the user_object but I have to then go and read from the OData service i.e. another server hit in order to retrieve the user_object_attributes.
Upon returning I have the id that would go neatly into the child user_object_attribute object but what if the child had a further server-side generated field like created_date? Then I still have to hit the OData repo again?
I've tried the correct $expand query ?$expand=user_objects_attributes and while the creation succeeds I still am not returned the expanded result set.
Would anyone have any idea if this is possible? Or is it a case that it isn't because hte only way to do it would be to return all the child user_object_attributes?
{
"annotator_id":1,
"content_type":"content_type",
"created_date":"2013-02-15T17:20:09.191",
"guid":"GUID",
"size_kb":100,
"title":"Title",
"updated_date":null,
"url":"URL",
"user_object_id":0,
"user_objects_attributes":[
{
"attribute_id":0,
"name":"name191",
"user_object_id":0,
"value":"value191"
}
]
}
Thanks,
Mark.
Looking at the OData V3 specification here:
In version 1.0 and version 2.0, if the insert succeeds in full, the
server MUST return a response with a 201 (Created) status code and a
response body that conforms to the syntax specified in InsertEntity
Request (section 2.2.7.1.1). The response body MUST contain the values
of the inserted resource after the server has executed all its
server-specific data processing rules (validation, and so on). The
server MAY alter the values of the resource received from the client
before the resource is inserted on the server.
In version 3.0, the response MAY have a 204 status code, as specified
in [RFC2616], based on the client preference (see Prefer (section
2.2.5.9)) on the InsertEntity Request.
Not super clear what a server should do... return just the top level created entity, or that entity and all of it's expanded links as well.
I'm not surprised that $expand would have no effect on POST (or any CUD requests). WCF DS probably ignores it if it isn't a query. And by the spec, this is probably correct.
Quite frankly, I think with WCF DS Server you can't get anything else back. In V3 you either get a 201 with your entity (only), or a 204 no content if you specify the header saying that you want no content.
Whether that is OData compliant or not... not totally sure :-). Regardless, I don't think you can get what you want on the WCF DS stack at this point in time.
I was able to return navigation properties after a POST to create a new entity using OData v4's $expand query option without issues.
Make sure your method is decorated with [EnableQuery] and you call Include with the name of the navigation property.
For example:
[EnableQuery]
public IHttpActionResult Post(user_object user)
{
if (!this.ModelState.IsValid)
{
return this.BadRequest(this.ModelState);
}
this.db.user_objects.Add(user);
this.db.SaveChanges();
user = this.db.user_objects.Include("user_objects_attributes").Single(x => x.user_object_id == user.user_object_id));
return this.Created(user);
}
The POST URL query string should include ?$expand=user_objects_attributes.
Also see:
https://github.com/OData/WebApi/issues/356

Resources