Successfactors ODATA update entities with one to many relation - odata

im trying to use Successfactors' ODATA API to update an entity.
This entity has got a one to many relationship to another entity.
The Model looks like:
Candidate
- custAnrede (PicklistOption (1:*)
I try to call
PUT <server>/odata/v2/Candidate('myId')
data:
{"custAnrede" : {"id":"555"}}
}
This call fails with:
Inline entity are not supported for property custAnrede in non insert request.
When calling with data:
{"custAnrede": {
"__metadata": {
"uri": "PicklistOption('HRUser')"
}
}}
it suceeds, but the value of custAnrede has not been changed.
Does anybody know how a one to many relationship with ODATA can be modified ?
Thanks,
Detlef

I've done something similar but with different objects (Parent - Child)
{
"__metadata":{
"uri":"cust_Overeenkomst(cust_ParentOvereenkomst_effectiveStartDate=datetime'2017-01-12T00:00:00',cust_ParentOvereenkomst_externalCode='1109',externalCode=2502L)"
},
"cust_event": "SO",
"cust_eventreason":"SO-01",
"cust_to_childsalarisgegevens":{
"__metadata":{
"uri":"cust_ChildSalarisgegevens(cust_Overeenkomst_externalCode=2502L,cust_ParentOvereenkomst_effectiveStartDate=datetime'2017-01-12T00:00:00',cust_ParentOvereenkomst_externalCode='1109',externalCode='SalComp_26')"
},
"cust_opmerking":"TEST Vincent",
"cust_paycomponent":"BRUTOMAAND",
"cust_paycompvalue":"1000"
}
}

Related

BreezeJS: Why aren't deletions made in Before/AfterSaveEntitiesDelegate propagated back to the client?

I am writing a simple event planning web app (using BreezeJS/Entity Framework) - users create a tournament entity, and ask the server to generate one or more suggested plans (just one for the purposes of this post).
Whenever the user clicks "generate plan", the tournament (including lots of details needed to generate a plan) should be submitted to the server, the server should delete any existing plan, generate a new one, and the client-side model should be updated.
A perfect fit for a named save, I thought!
The problem is the last step: updating the client-side model. The plan entity added by the server appears as expected in the client, but the deletion is ignored. I.e. the client ends up with both the new and the old plan!
Here's my named save:
[Note: The description and code in this question omits a lot of irrelevant details (like 20 properties and entity types) to keep the size of the question down]
[HttpPost]
public SaveResult MyNamedSave(JObject saveBundle)
{
_contextProvider.BeforeSaveEntitiesDelegate = RecalculatePlan;
return _contextProvider.SaveChanges(saveBundle);
}
private Dictionary<Type, List<EntityInfo>> RecalculatePlan(Dictionary<Type, List<EntityInfo>> arg)
{
// See https://stackoverflow.com/questions/14517945/using-this-context-inside-beforesaveentity:
var readonlyContext = new PontifexContext();
foreach (var eventInfo in arg[typeof(Tournament)])
{
var tournament = (Tournament)eventInfo.Entity;
var deletePlan = readonlyContext.Plans.First(p => p.TournamentId == tournament.Id);
arg[typeof(Plan)].Add(_contextProvider.CreateEntityInfo(deletePlan, EntityState.Deleted););
var addPlan = new Plan {TournamentId = tournament.Id, };
arg[typeof(Plan)].Add(_contextProvider.CreateEntityInfo(addPlan, EntityState.Added););
}
}
Am I trying to use named-saves for something they're not meant to do (i.e. deleting and adding entities)?
PS: I tried doing an explicit addition and save using both readonlyContext and _contextProvider.Context, but that really didn't work.
EDIT:
If I try to explicitly delete the old plan from the DB like below, nothing happens:
arg[typeof(Plan)].Add(_contextProvider.CreateEntityInfo(deletePlan, EntityState.Deleted););
// Add this:
context.PlanEntries.Remove(deletePlan);
context.SaveChanges();
I'm guessing it's because _contextProvider.Context already has the old plan in cache, so deleting it "behind its back" (i.e. using another context) doesn't make a difference.
If I then try removing it using _contextProvider.Context, I get a weird duplicate-entry error from the framework.
I'm at my wits' end!
EDIT 2:
Here's the data in the save request and response, as logged by IEs developer tools.
Request first:
{
"entities": [
{
"Id": 1,
"EventName": "Test Tournament",
"EventTime": "2015-03-21T20:00:00.000Z",
"entityAspect": {
"entityTypeName": "Tournament:#Pontifex.Model",
"defaultResourceName": "Tournaments",
"entityState": "Unchanged",
"originalValuesMap": { },
"autoGeneratedKey": {
"propertyName": "Id",
"autoGeneratedKeyType": "Identity"
}
}
}
],
"saveOptions": { }
}
The server then deletes the existing Plan entry (Id=10), and adds a new (Id=11), which I verified using a SELECT directly in the DB. That is good.
But the response is:
[
{
"$id": "1",
"$type": "Pontifex.Model.Tournament, Pontifex.Server",
"Id": 1,
"EventName": "Test Tournament",
"EventTime": "2015-03-21T20:00:00.000",
"Plans": [
{
"$id": "17",
"$type": "Pontifex.Model.Plan, Pontifex.Server",
"Id": 11,
"TournamentId": 1,
"Tournament": { "$ref": "1" }
}
],
"BoardPlan": null
}
]
In this response, the deleted entity never appears, so the client understandably leaves it in its model.
The added Plan (Id 11) does appear, and is integrated in the client model.
BUT: judging from sbelinis answer to Server added object showing as added in client after save changes, the fact that the added Plan appears may be a lucky coincidence:
In your particular example, the new entity made into the save because it happened to be related to the entity of the BeforeSaveEntity method, but you should not rely on it.
But sbelinis example of how to properly add an entity appears incomplete (e.g. it refers to a local variable saveMapAdditions which isn't used elsewhere)
OK, I figured out how to work around this!
I still can't get the deletion reflected back into the client cache...BUT if my server-side code ALSO removes the deleted entity from all relations, the removals WILL be reflected back, and the entity will disappear from the client-side model.
The updated code (I've added the statement tournament.Plans.Remove(deletePlan)):
[HttpPost]
public SaveResult MyNamedSave(JObject saveBundle)
{
_contextProvider.BeforeSaveEntitiesDelegate = RecalculatePlan;
return _contextProvider.SaveChanges(saveBundle);
}
private Dictionary<Type, List<EntityInfo>> RecalculatePlan(Dictionary<Type, List<EntityInfo>> arg)
{
// See http://stackoverflow.com/questions/14517945/using-this-context-inside-beforesaveentity:
var readonlyContext = new PontifexContext();
foreach (var eventInfo in arg[typeof(Tournament)])
{
var tournament = (Tournament)eventInfo.Entity;
var deletePlan = readonlyContext.Plans.First(p => p.TournamentId == tournament.Id);
arg[typeof(Plan)].Add(_contextProvider.CreateEntityInfo(deletePlan, EntityState.Deleted););
// Workaround: Remove the deleted plan from all relations:
tournament.Plans.Remove(deletePlan);
var addPlan = new Plan {TournamentId = tournament.Id, };
arg[typeof(Plan)].Add(_contextProvider.CreateEntityInfo(addPlan, EntityState.Added););
}
}
Of course, if you search the client local cache for Plan entities, I suspect the deleted plan will still appear, so it's not perfect. But it works for me!

breeze: creating client-side entities with data coming from third-party service

I'm getting data from a third-party service and while I had no issues converting to breeze entities, I have one particular scenario that puzzles me:
the data structure I receive is this one (simplified for the sake of clarity)
{
TotalRecords: 72,
Contractors: [ { name: 'test} , {name: 'test2'}]
}
in my jsonResultAdpater, I have created an extractResults method, which returns data.results.Contractors.
And in my visitNode method, I can convert objects of the Contactors array to breeze entities.
But I've lost the TotalRecords property on the way.... This should be passed somehow to the controller that initiated the call to the third-party webservice.
How would I do that ?
adapter:
extractResults: function (data) {
var results = data.results;
return results && results.Contractors
},
visitNode: function (node, parseContext, nodeContext) {
if (node && node.Type === 'ContractorFrameworkDTO') {
return { entityType: "Freelancer" };
}
}
actually it was as simple as filling the inlineCount property in the extractResults method:
data.inlineCount = results.TotalRecords;

BreezeJS - Using expand

I am querying the server to get an entity with expand
function _loadIncidents() {
var deffered = Q.defer(),
queryObj = new breeze.EntityQuery().from('Incidents').expand(['Deployments', 'IncidentComments', 'DTasks', 'ExtendedProperties', 'IncidentEvents']);
dataRepository.fetchEntitiesByQuery(queryObj, true).then(function (incidents) {
var query = breeze.EntityQuery.from("DTasks"),
incidentIds = dataRepository.getEntitiesByQuerySync(query);
deffered.resolve();
}, function(err) {
deffered.reject(err);
});
return deffered.promise;
};
I am getting the results and all is fine, how ever when I query breeze cache to get the entities - I am getting empty collection. So when using expand does the expanded entities are added to the cache?
Yes the related entities identified in the expand should be in cache ... if the query is "correct" and the server interpreted your request as you intended.
Look at the payload of the response from the first request. Are the related entities present? If not, perhaps the query was not well received on the server. As a general rule, you want to make sure the data are coming over the wire before wondering whether Breeze is doing the right thing with those data.
I do find myself wondering about the spelling of the items in your expand list. They are all in PascalCase. Are they these the names of navigation properties of the Incident type? Or are they the names of the related EntityTypes? They need to be former (nav property names), not the latter.
I Had problem with the navigation property - as I am not using OData webapi not using EF , there is problem with the navigation properties so for the current time i just wrote
Object.defineProperty(this, 'Deployments', {
get: function () {
return (this.entityAspect && this.entityAspect.entityManager) ?
this.entityAspect.entityManager.executeQueryLocally(new breeze.EntityQuery("Deployments").
where('IncidentID', 'eq', this.IncidentID)) :
[];
},
set: function (value) { //used only when loading incidents from the server
if (!value.results) {
return;
}
var i = 0,
dataRepository = require('sharedServices/dataRepository');
for (i; i < value.results.length; i++) {
dataRepository.addUnchangedEntity('Deployment', value.results[i]);
}
},
enumerable: true
});

How to filter the tasks using custom field value - ProjectServer Rest OData API 2013

I've been trying to filter the Task collection using custom field called "TaskType" (I've created this one). This custom field has an associated lookup table. So if i query the OData :
https://myserver/PWA/_api/ProjectServer/Projects('0647003e-dce3-e211-9477-080027efb62d')/IncludeCustomFields/Tasks
I get the following custom field data:
"Custom_x005f_fe7ff100cee3e2119477080027efb62d":{
"results":[
"Entry_960a8ffef3334cc2bfee14a27cf95dbb"
]
}
since this is an array I can't filter using this field. I can extract names of the assigned custom fields, but not the value.
so the following query
https://PWA/_api/ProjectServer/Projects('0647003e-dce3-e211-9477-080027efb62d')/IncludeCustomFields/Tasks?$select=CustomFields/Name,CustomFields/LookupEntries/Value&$expand=CustomFields,CustomFields/LookupEntries
would return something like this
{
"CustomFields":{
"results":[
{
"LookupEntries":{
"results":[
{
"Value":"Normal"
},
{
"Value":"Auto"
},
{
"Value":"Manual"
}
]
},
"Name":"Task Type"
}
]
}
}
Is there any association between Task and its Custom Field Value so that could be used to filter the results?
Using the ProjectData API is not something I want to go into.

Post multiple parameters to MVC Controller using jQuery.post

I have a controller defined as:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult PostMoreData(DataContracts.Address address, DataContracts.GeoLocation geoLocation)
{
return Json("test");
}
where DataContracts.Address and DataContracts.GeoLocation are complex types.
From my View i'm trying to post using jQuery as such:
function PostMoreData() {
var JsonAddress = {
"Building": $('Building').val(),
"UnitNumber": $('UnitNumber').val(),
"StreetNumber": $('StreetNumber').val(),
"StreetName": $('StreetName').val(),
"StreetType": $('StreetType').val(),
"Suburb": $('Suburb').val(),
"State": $('State').val(),
"Postcode": $('Postcode').val(),
"MonthsAtAddress": $('MonthsAtAddress').val()
};
var JsonGeoLocation = {
"Latitude": $('Latitude').val(),
"Longitude": $('Longitude').val()
};
jQuery.post("/AddressValidation/PostMoreData", {address: JsonAddress, geoLocation: JsonGeoLocation}, function(data, textStatus) {
if (textStatus == "success") {
var result = eval(data);
if (result.length > 0) {
alert(result);
}
}
}, "json");
}
However, on the controller, I get nulls.
It works if my Controller takes just 1 argument and I post just one object.
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult PostMoreData(DataContracts.Address address)
{
return Json("test");
}
function PostMoreData() {
var JsonAddress = {
"Building": $('Building').val(),
"UnitNumber": $('UnitNumber').val(),
"StreetNumber": $('StreetNumber').val(),
"StreetName": $('StreetName').val(),
"StreetType": $('StreetType').val(),
"Suburb": $('Suburb').val(),
"State": $('State').val(),
"Postcode": $('Postcode').val(),
"MonthsAtAddress": $('MonthsAtAddress').val()
};
jQuery.post("/AddressValidation/PostMoreData", JsonAddress, function(data, textStatus) {
if (textStatus == "success") {
var result = eval(data);
if (result.length > 0) {
alert(result);
}
}
}, "json");
}
Any ideas how i can post more than one object?
Note that the "default serialization" that jQuery is doing here isn't going to work no matter what your controller does. jQuery doesn't "traverse" the parameter map below the first level, so the example in the question is likely generating this post data:
address=[object]&geoLocation=[object]
The other, working example does not contain any sub-objects, so it is being translated directly, like this:
Building=value&UnitNumber=value&...&MonthsAtAddress=value
The easiest fix is making the parameter map flat, each key prefixed with either 'Address.' or 'GeoLocation.', depending.
Thank you everyone for your input on this issue.
At this stage, we have departed from using jquery to post complex types to controllers. Instead we use the ms ajax framework to do that. ms ajax post nicely binds the complex types automatically out of the box.
So our solution now uses a mix of jquery and ms ajax framework.
Ash
Your code requires that the way jquery serializes an object is compatible with the MVC default model binder, which I think is unlikely.
If you can build your javascript object so that it serializes as a flat object with dot notation (JsonAddress.Building) that would work, or you can let jquery do the default serialization and then create a custom model binder to deserialize to the action parameter types.
I had the same problem and couldn't get anything to work. Also someone raised it as a bug with jquery and they closed it as not a bug.
I have found a few solutions which answer part of the whole question.
And the answer includes the following.
1) Client side: we would need to stringyfy all the objects you need to send. This could be a normal object or an array. It works on both.
2) Client side: You send the data as you have in the first post. As you would object by object.
Tip: When you send parameterised objects, jquery encodes the data sent to the server.
Following all are server side implementations
1) Deserializer class: which will take the input string and put it back in to object/list<>/IList<> what ever you have defined as datatype of the parameter of the controller function.
You would need to implement ActionFilterAttribute for the above.
2) Finally add an attribute to controller function, so that it uses the deserialiser class to get the parameters.
As this is quite a lot of code let me know if you need details or have you solved the problem.
Deepak Chawla

Resources