Skip to content

Commit d878b73

Browse files
authored
Update collections.md
1 parent 9137756 commit d878b73

File tree

1 file changed

+51
-48
lines changed

1 file changed

+51
-48
lines changed

graph/articles/collections.md

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -321,77 +321,79 @@ Content-Type: application/json
321321

322322
Collections of entity types are generally preferable to collections of structual types because collections of structural types must be updated as a single unit, meaning that they are overwritten entirely by new contents, rather than be updated relative to the existing contents.
323323
Sometimes, structural collection properties are added to a type and then scenarios are discovered later that require a collection of entity types.
324-
Take the following model with an entity type `foo` that has a collection of `bar`s:
324+
Take the following model with an entity type `application` that has a collection of `keyCredential`s:
325325

326326
```xml
327-
<EntityType Name="foo">
327+
<EntityType Name="application">
328328
<Key>
329329
<PropertyRef Name="id" />
330330
</Key>
331331
<Property Name="id" Type="Edm.String" Nullable="false" />
332-
<Property Name="bars" Type="Collection(self.bar)" />
332+
<Property Name="keyCredentials" Type="Collection(self.keyCredential)" />
333+
...
333334
</EntityType>
334335

335-
<ComplexType Name="bar">
336-
<Property Name="prop1" Type="Edm.String" />
337-
<Property Name="prop2" Type="Edm.String" />
336+
<ComplexType Name="keyCredential">
337+
<Property Name="keyId" Type="Edm.Guid" />
338+
<Property Name="endDateTime" Type="Edm.DateTimeOffset" />
339+
...
338340
</ComplexType>
339341
```
340-
and a scenario arises that requires, for example, to remove individual `bar`s from the collection.
342+
and a scenario arises that requires, for example, to remove individual `keyCredential`s from the collection.
341343
There are two options forward: //// TODO do we want to offer both options, or just one?
342344

343345
### 11.1 Side-by-side collection properties
344346

345347
The model can be updated to have two collections side-by-side:
346348
```diff
347-
<EntityType Name="foo">
349+
<EntityType Name="application">
348350
<Key>
349351
<PropertyRef Name="id" />
350352
</Key>
351353
<Property Name="id" Type="Edm.String" Nullable="false" />
352-
<Property Name="bars" Type="Collection(self.bar)" />
353-
+ <NavigationProperty Name="barsAsEntities" Type="Collection(self.barAsEntity)" ContainsTarget="true" />
354+
<Property Name="keyCredentials" Type="Collection(self.keyCredential)" />
355+
+ <NavigationProperty Name="keyCredentialsAsEntities" Type="Collection(self.keyCredentialAsEntity)" ContainsTarget="true" />
354356
</EntityType>
355357

356-
<ComplexType Name="bar">
357-
<Property Name="prop1" Type="Edm.String" />
358-
<Property Name="prop2" Type="Edm.String" />
358+
<ComplexType Name="keyCredential">
359+
<Property Name="keyId" Type="Edm.Guid" />
360+
<Property Name="endDateTime" Type="Edm.DateTimeOffset" />
359361
</ComplexType>
360362

361-
+<EntityType Name="barAsEntity">
363+
+<EntityType Name="keyCredentialAsEntity">
362364
+ <Key>
363-
+ <PropertyRef Name="prop1" />
365+
+ <PropertyRef Name="keyId" />
364366
+ </Key>
365-
+ <Property Name="prop1" Type="Edm.String" />
366-
+ <Property Name="prop2" Type="Edm.String" />
367+
+ <Property Name="keyId" Type="Edm.Guid" />
368+
+ <Property Name="endDateTime" Type="Edm.DateTimeOffset" />
367369
+</EntityType>
368370
```
369-
Clients will now be able to refer to individual `bar`s using `prop1` as a key, and they can now remove those `bar`s using `DELETE` requests:
371+
Clients will now be able to refer to individual `keyCredential`s using `keyId` as a key, and they can now remove those `keyCredential`s using `DELETE` requests:
370372
```http
371-
DELETE /foos/{fooId}/bars/{some_prop1}
373+
DELETE /applications/{applicationId}/keyCredentials/{some_keyId}
372374
```
373375
```http
374376
HTTP/1.1 204 No Content
375377
```
376-
The expectation is that `bars` and `barsAsEntities` are treated as two "views" into the same data.
378+
The expectation is that `keyCredentials` and `keyCredentialsAsEntities` are treated as two "views" into the same data.
377379
To meet this expectation, workloads must:
378-
1. Keep the properties consistent between `bar` and `barAsEntity`.
380+
1. Keep the properties consistent between `keyCredential` and `keyCredentialAsEntity`.
379381
Any changes to one type must be reflected in the other type.
380382
2. Reject requests that update both collections at the same time.
381-
A request that adds an item to `barsAsEntities` while replacing the content of `bars` must rejected with a `400`, for example:
383+
A request that adds an item to `keyCredentialsAsEntities` while replacing the content of `keyCredentials` must rejected with a `400`, for example:
382384
```http
383-
PATCH /foos/{fooId}
385+
PATCH /applications/{applicationId}
384386
{
385-
"bars": [
387+
"keyCredentials": [
386388
{
387-
"prop1": "some value",
388-
"prop2": "another value"
389+
"keyId": "10000000-0000-0000-0000-000000000000",
390+
"endDateTime": "2012-12-03T07:16:23Z"
389391
}
390392
],
391-
"barsAsEntities@delta": [
393+
"keyCredentialsAsEntities@delta": [
392394
{
393-
"prop1": "a key value",
394-
"prop2": "some new value"
395+
"keyId": "20000000-0000-0000-0000-000000000000",
396+
"endDateTime": "2012-12-03T07:16:23Z"
395397
}
396398
]
397399
}
@@ -401,7 +403,7 @@ HTTP/1.1 400 Bad Request
401403
{
402404
"error": {
403405
"code": "badRequest",
404-
"message": "'bars' and 'barsAsEntities' cannot be updated in the same request.",
406+
"message": "'keyCredentials' and 'keyCredentialsAsEntities' cannot be updated in the same request.",
405407
}
406408
```
407409

@@ -412,52 +414,53 @@ TODO implement this in WebApi
412414

413415
The model can be updated to simply switch the complex type for an entity type:
414416
```diff
415-
<EntityType Name="foo">
417+
<EntityType Name="application">
416418
<Key>
417419
<PropertyRef Name="id" />
418420
</Key>
419421
<Property Name="id" Type="Edm.String" Nullable="false" />
420-
- <Property Name="bars" Type="Collection(self.bar)" />
421-
+ <NavigationProperty Name="bars" Type="Collection(self.bar)" ContainsTarget="true" />
422+
- <Property Name="keyCredentials" Type="Collection(self.keyCredential)" />
423+
+ <NavigationProperty Name="keyCredentials" Type="Collection(self.keyCredential)" ContainsTarget="true" />
422424
</EntityType>
423425

424-
- <ComplexType Name="bar">
425-
+ <EntityType Name="bar">
426+
- <ComplexType Name="keyCredential">
427+
+ <EntityType Name="keyCredential">
426428
+ <Key>
427-
+ <PropertyRef Name="prop1" />
429+
+ <PropertyRef Name="keyId" />
428430
+ </Key>
429-
<Property Name="prop1" Type="Edm.String" />
430-
<Property Name="prop2" Type="Edm.String" />
431+
<Property Name="keyId" Type="Edm.Guid" />
432+
<Property Name="endDateTime" Type="Edm.DateTimeOffset" />
431433
-</ComplexType>
432434
+</EntityType>
433435
```
434436
To maintain backwards compatibility **and** compliance with the OData standard, there are several semantic changes that the workload must address:
435-
1. Existing clients would have been able to `$select` the `bars` property.
436-
Now that `bars` is a navigation property, the [OData standard](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#_Toc31361040) specifies that its navigation link be returned when it is `$selected`:
437+
1. Existing clients would have been able to `$select` the `keyCredentials` property.
438+
Now that `keyCredentials` is a navigation property, the [OData standard](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#_Toc31361040) specifies that its navigation link be returned when it is `$selected`:
437439

438440
> If the select item is a navigation property, then the corresponding navigation link is represented in the response.
439441
440-
Because the previous behavior for `$select=bars` was to include the collection in the response, and because the standard dictates that the navigation link be included in the response, the new behavior is to include both:
442+
Because the previous behavior for `$select=keyCredentials` was to include the collection in the response, and because the standard dictates that the navigation link be included in the response, the new behavior is to include both:
441443

442444
```http
443-
GET /foos/{fooId}?$select=bars
445+
GET /applications/{applicationId}?$select=keyCredentials
444446
```
445447
```http
446448
200 OK
447449
{
448-
"id": "{fooId}",
449-
"bars": [
450+
"id": "{applicationId}",
451+
"keyCredentials": [
450452
{
451-
"prop1": "some value",
452-
"prop2": "another value"
453+
"keyId": "30000000-0000-0000-0000-000000000000",
454+
"endDateTime": "2012-12-03T07:16:23Z",
455+
...
453456
},
454457
...
455458
]
456-
"bars@odata.navigationLink": "/foos('{fooId}')/bars"
459+
"keyCredentials@odata.navigationLink": "/applications('{applicationId}')/keyCredentials"
457460
}
458461
```
459462

460-
2. The default behavior for structural collections is to include them in the response payload for their containing entity. If this was the behavior of `foo` before, it must be preserved by **auto-expanding** the `bars` property now that it is a navigation property (because the default behavior for navigation properties is to **not** expand them).
463+
2. The default behavior for structural collections is to include them in the response payload for their containing entity. If this was the behavior of `application` before, it must be preserved by **auto-expanding** the `keyCredentials` property now that it is a navigation property (because the default behavior for navigation properties is to **not** expand them).
461464
3. Structural collections are updated using `PATCH` requests to replace the entire contents of the collection. The new navigation property must preserve this behavior.
462465

463466
TODO implement this in webapi

0 commit comments

Comments
 (0)