Skip to content

Commit 6f5bc5e

Browse files
authored
Don't require entityset when writing responses (#3137)
* Don't require entityset when writing responses * Clean up code to remove throwIfMissing and address review comments.
1 parent 9ce546b commit 6f5bc5e

22 files changed

+128
-162
lines changed

src/Microsoft.OData.Client/ConventionalODataEntityMetadataBuilder.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ internal ConventionalODataEntityMetadataBuilder(UriResolver resolver, string ent
6868
/// </returns>
6969
internal override Uri GetEditLink()
7070
{
71+
if(this.entitySetName == null)
72+
{
73+
return null;
74+
}
75+
7176
Uri entitySetUri = this.uriBuilder.BuildEntitySetUri(this.baseUri, this.entitySetName);
7277
Uri editLink = this.uriBuilder.BuildEntityInstanceUri(entitySetUri, this.entityInstance);
7378
return editLink;

src/Microsoft.OData.Core/Evaluation/ODataConventionalEntityMetadataBuilder.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,12 @@ internal override string GetOperationTitle(string operationName)
393393
/// </returns>
394394
internal override bool TryGetIdForSerialization(out Uri id)
395395
{
396+
if(this.ResourceMetadataContext.TypeContext.NavigationSourceName == null)
397+
{
398+
id = null;
399+
return false;
400+
}
401+
396402
id = this.ResourceMetadataContext.Resource.IsTransient ? null : this.GetId();
397403
return true;
398404
}
@@ -405,10 +411,13 @@ private Uri ComputeEditLink()
405411
{
406412
Uri uri = this.ResourceMetadataContext.Resource.HasNonComputedId ? this.ResourceMetadataContext.Resource.NonComputedId : this.ComputedId;
407413

408-
Debug.Assert(this.ResourceMetadataContext != null && this.ResourceMetadataContext.TypeContext != null, "this.resourceMetadataContext != null && this.resourceMetadataContext.TypeContext != null");
409-
if (this.ResourceMetadataContext.ActualResourceTypeName != this.ResourceMetadataContext.TypeContext.NavigationSourceEntityTypeName)
414+
if (uri != null)
410415
{
411-
uri = this.UriBuilder.AppendTypeSegment(uri, this.ResourceMetadataContext.ActualResourceTypeName);
416+
Debug.Assert(this.ResourceMetadataContext != null && this.ResourceMetadataContext.TypeContext != null, "this.resourceMetadataContext != null && this.resourceMetadataContext.TypeContext != null");
417+
if (this.ResourceMetadataContext.ActualResourceTypeName != this.ResourceMetadataContext.TypeContext.NavigationSourceEntityTypeName)
418+
{
419+
uri = this.UriBuilder.AppendTypeSegment(uri, this.ResourceMetadataContext.ActualResourceTypeName);
420+
}
412421
}
413422

414423
return uri;

src/Microsoft.OData.Core/Evaluation/ODataConventionalIdMetadataBuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,11 @@ private void ComputeAndCacheId()
131131
case EdmNavigationSourceKind.Singleton:
132132
uri = this.ComputeIdForSingleton();
133133
break;
134+
// Treat UnknownEntitySet as a containment
135+
case EdmNavigationSourceKind.UnknownEntitySet:
134136
case EdmNavigationSourceKind.ContainedEntitySet:
135137
uri = this.ComputeIdForContainment();
136138
break;
137-
case EdmNavigationSourceKind.UnknownEntitySet:
138-
throw new ODataException(Strings.ODataMetadataBuilder_UnknownEntitySet(this.ResourceMetadataContext.TypeContext.NavigationSourceName));
139139
default:
140140
uri = this.ComputeId();
141141
break;
@@ -152,7 +152,7 @@ private void ComputeAndCacheId()
152152
/// </returns>
153153
private Uri ComputeId()
154154
{
155-
if (this.ResourceMetadataContext.KeyProperties.Any())
155+
if (this.ResourceMetadataContext.TypeContext.NavigationSourceName != null && this.ResourceMetadataContext.KeyProperties.Any())
156156
{
157157
Uri uri = this.UriBuilder.BuildBaseUri();
158158
uri = this.UriBuilder.BuildEntitySetUri(uri, this.ResourceMetadataContext.TypeContext.NavigationSourceName);

src/Microsoft.OData.Core/Evaluation/ODataMetadataContext.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,7 @@ public ODataResourceMetadataBuilder GetResourceMetadataBuilderForReader(IODataJs
267267
IEdmEntityType navigationSourceElementType = this.edmTypeResolver.GetElementType(navigationSource);
268268
IODataResourceTypeContext typeContext =
269269
ODataResourceTypeContext.Create( /*serializationInfo*/
270-
null, navigationSource, navigationSourceElementType, resourceState.ResourceTypeFromMetadata ?? resourceState.ResourceType,
271-
/*throwIfMissingTypeInfo*/ true);
270+
null, navigationSource, navigationSourceElementType, resourceState.ResourceTypeFromMetadata ?? resourceState.ResourceType);
272271

273272
IODataResourceMetadataContext resourceMetadataContext = ODataResourceMetadataContext.Create(
274273
resource,

src/Microsoft.OData.Core/Json/ODataJsonWriter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1838,7 +1838,7 @@ await this.WriteV4DeletedEntryContentsAsync(resource)
18381838
string expectedNavigationSource = deltaResourceSetScope?.NavigationSource?.Name;
18391839
string currentNavigationSource = resource.SerializationInfo?.NavigationSourceName ?? resourceScope.NavigationSource?.Name;
18401840

1841-
if (String.IsNullOrEmpty(currentNavigationSource) || currentNavigationSource != expectedNavigationSource)
1841+
if (!String.IsNullOrEmpty(currentNavigationSource) && currentNavigationSource != expectedNavigationSource)
18421842
{
18431843
Debug.Assert(this.ScopeLevel <= 3, "Writing a nested deleted resource of the wrong type should already have been caught.");
18441844

src/Microsoft.OData.Core/ODataContextUriBuilder.cs

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -172,25 +172,40 @@ private Uri CreateFromContextUrlInfo(ODataContextUrlInfo info)
172172
}
173173
else
174174
{
175-
// No path information
176-
switch (info.DeltaKind)
177-
{
178-
case ODataDeltaKind.ResourceSet:
179-
return new Uri(ODataConstants.ContextUriFragmentIndicator + ODataConstants.DeltaResourceSet, UriKind.Relative);
180-
case ODataDeltaKind.DeletedEntry:
181-
return new Uri(ODataConstants.ContextUriFragmentIndicator + ODataConstants.DeletedEntry, UriKind.Relative);
182-
case ODataDeltaKind.Link:
183-
return new Uri(ODataConstants.ContextUriFragmentIndicator + ODataConstants.DeltaLink, UriKind.Relative);
184-
case ODataDeltaKind.DeletedLink:
185-
return new Uri(ODataConstants.ContextUriFragmentIndicator + ODataConstants.DeletedLink, UriKind.Relative);
186-
}
187-
188175
if (!string.IsNullOrEmpty(info.TypeName))
189176
{ // #TypeName
190177
builder.Append(info.TypeName);
178+
179+
switch (info.DeltaKind)
180+
{
181+
case ODataDeltaKind.ResourceSet:
182+
builder.Append(ODataConstants.UriSegmentSeparatorChar + ODataConstants.DeltaResourceSet);
183+
break;
184+
case ODataDeltaKind.DeletedEntry:
185+
builder.Append(ODataConstants.UriSegmentSeparatorChar + ODataConstants.DeletedEntry);
186+
break;
187+
case ODataDeltaKind.Link:
188+
builder.Append(ODataConstants.UriSegmentSeparatorChar + ODataConstants.DeltaLink);
189+
break;
190+
case ODataDeltaKind.DeletedLink:
191+
builder.Append(ODataConstants.UriSegmentSeparatorChar + ODataConstants.DeletedLink);
192+
break;
193+
}
191194
}
192195
else
193196
{
197+
switch (info.DeltaKind)
198+
{
199+
case ODataDeltaKind.ResourceSet:
200+
return new Uri(ODataConstants.ContextUriFragmentIndicator + ODataConstants.DeltaResourceSet, UriKind.Relative);
201+
case ODataDeltaKind.DeletedEntry:
202+
return new Uri(ODataConstants.ContextUriFragmentIndicator + ODataConstants.DeletedEntry, UriKind.Relative);
203+
case ODataDeltaKind.Link:
204+
return new Uri(ODataConstants.ContextUriFragmentIndicator + ODataConstants.DeltaLink, UriKind.Relative);
205+
case ODataDeltaKind.DeletedLink:
206+
return new Uri(ODataConstants.ContextUriFragmentIndicator + ODataConstants.DeletedLink, UriKind.Relative);
207+
}
208+
194209
return null;
195210
}
196211
}

src/Microsoft.OData.Core/ODataContextUrlInfo.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,12 +238,14 @@ internal static ODataContextUrlInfo Create(ODataResourceTypeContext typeContext,
238238
{
239239
Debug.Assert(typeContext != null, "typeContext != null");
240240

241+
string typeName = typeContext.NavigationSourceEntityTypeName ?? typeContext.ExpectedResourceTypeName;
242+
241243
ODataContextUrlInfo contextUriInfo = new ODataContextUrlInfo()
242244
{
243245
IsUnknownEntitySet = typeContext.NavigationSourceKind == EdmNavigationSourceKind.UnknownEntitySet,
244246
NavigationSource = typeContext.NavigationSourceName,
245-
TypeCast = typeContext.NavigationSourceEntityTypeName == typeContext.ExpectedResourceTypeName ? null : typeContext.ExpectedResourceTypeName,
246-
TypeName = typeContext.NavigationSourceEntityTypeName,
247+
TypeCast = typeName == typeContext.ExpectedResourceTypeName ? null : typeContext.ExpectedResourceTypeName,
248+
TypeName = EdmLibraryExtensions.GetCollectionTypeName(typeName),
247249
IncludeFragmentItemSelector = kind == ODataDeltaKind.Resource && typeContext.NavigationSourceKind != EdmNavigationSourceKind.Singleton,
248250
DeltaKind = kind,
249251
NavigationPath = ComputeNavigationPath(typeContext.NavigationSourceKind, null, typeContext.NavigationSourceName),

src/Microsoft.OData.Core/ODataResourceTypeContext.cs

Lines changed: 9 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -25,54 +25,36 @@ internal class ODataResourceTypeContext : IODataResourceTypeContext
2525
/// </summary>
2626
protected string expectedResourceTypeName;
2727

28-
/// <summary>
29-
/// If true, throw if any of the set or type name cannot be determined; if false, return null when any of the set or type name cannot determined.
30-
/// </summary>
31-
private readonly bool throwIfMissingTypeInfo;
32-
3328
/// <summary>
3429
/// Constructs an instance of <see cref="ODataResourceTypeContext"/>.
3530
/// </summary>
36-
/// <param name="throwIfMissingTypeInfo">If true, throw if any of the set or type name cannot be determined; if false, return null when any of the set or type name cannot determined.</param>
37-
private ODataResourceTypeContext(bool throwIfMissingTypeInfo)
31+
private ODataResourceTypeContext()
3832
{
39-
this.throwIfMissingTypeInfo = throwIfMissingTypeInfo;
4033
}
4134

4235
/// <summary>
4336
/// Constructs an instance of <see cref="ODataResourceTypeContext"/>.
4437
/// </summary>
4538
/// <param name="expectedResourceType">The expected resource type of resource set or resource.</param>
46-
/// <param name="throwIfMissingTypeInfo">If true, throw if any of the set or type name cannot be determined; if false, return null when any of the set or type name cannot determined.</param>
47-
private ODataResourceTypeContext(IEdmStructuredType expectedResourceType, bool throwIfMissingTypeInfo)
39+
private ODataResourceTypeContext(IEdmStructuredType expectedResourceType)
4840
{
4941
this.expectedResourceType = expectedResourceType;
50-
this.throwIfMissingTypeInfo = throwIfMissingTypeInfo;
5142
}
5243

5344
/// <summary>
5445
/// The navigation source name of the resource set or resource.
5546
/// </summary>
56-
public virtual string NavigationSourceName
57-
{
58-
get { return this.ValidateAndReturn(default(string)); }
59-
}
47+
public virtual string NavigationSourceName { get; }
6048

6149
/// <summary>
6250
/// The entity type name of the navigation source of the resource set or resource.
6351
/// </summary>
64-
public virtual string NavigationSourceEntityTypeName
65-
{
66-
get { return this.ValidateAndReturn(default(string)); }
67-
}
52+
public virtual string NavigationSourceEntityTypeName { get; }
6853

6954
/// <summary>
7055
/// The full type name of the navigation source of the resource set or resource.
7156
/// </summary>
72-
public virtual string NavigationSourceFullTypeName
73-
{
74-
get { return this.ValidateAndReturn(default(string)); }
75-
}
57+
public virtual string NavigationSourceFullTypeName { get; }
7658

7759
/// <summary>
7860
/// The kind of the navigation source of the resource set or resource.
@@ -133,9 +115,8 @@ public virtual bool IsMediaLinkEntry
133115
/// <param name="navigationSource">The navigation source of the resource set or resource.</param>
134116
/// <param name="navigationSourceEntityType">The entity type of the navigation source.</param>
135117
/// <param name="expectedResourceType">The expected structured type of the resource set or resource.</param>
136-
/// <param name="throwIfMissingTypeInfo">If true, throw if any of the set or type name cannot be determined; if false, return null when any of the set or type name cannot determined.</param>
137118
/// <returns>A new instance of <see cref="ODataResourceTypeContext"/>.</returns>
138-
internal static ODataResourceTypeContext Create(ODataResourceSerializationInfo serializationInfo, IEdmNavigationSource navigationSource, IEdmEntityType navigationSourceEntityType, IEdmStructuredType expectedResourceType, bool throwIfMissingTypeInfo)
119+
internal static ODataResourceTypeContext Create(ODataResourceSerializationInfo serializationInfo, IEdmNavigationSource navigationSource, IEdmEntityType navigationSourceEntityType, IEdmStructuredType expectedResourceType)
139120
{
140121
if (serializationInfo != null)
141122
{
@@ -154,23 +135,7 @@ internal static ODataResourceTypeContext Create(ODataResourceSerializationInfo s
154135
return new ODataResourceTypeContextWithModel(navigationSource, navigationSourceEntityType, expectedResourceType);
155136
}
156137

157-
return new ODataResourceTypeContext(expectedResourceType, throwIfMissingTypeInfo);
158-
}
159-
160-
/// <summary>
161-
/// Validate and return the given value.
162-
/// </summary>
163-
/// <typeparam name="T">The type of the value to validate.</typeparam>
164-
/// <param name="value">The value to validate.</param>
165-
/// <returns>The return value.</returns>
166-
private T ValidateAndReturn<T>(T value) where T : class
167-
{
168-
if (this.throwIfMissingTypeInfo && value == null)
169-
{
170-
throw new ODataException(Strings.ODataResourceTypeContext_MetadataOrSerializationInfoMissing);
171-
}
172-
173-
return value;
138+
return new ODataResourceTypeContext(expectedResourceType);
174139
}
175140

176141
/// <summary>
@@ -188,7 +153,7 @@ internal sealed class ODataResourceTypeContextWithoutModel : ODataResourceTypeCo
188153
/// </summary>
189154
/// <param name="serializationInfo">The serialization info from the resource set or resource instance.</param>
190155
internal ODataResourceTypeContextWithoutModel(ODataResourceSerializationInfo serializationInfo)
191-
: base(/*throwIfMissingTypeInfo*/false)
156+
: base()
192157
{
193158
Debug.Assert(serializationInfo != null, "serializationInfo != null");
194159
this.serializationInfo = serializationInfo;
@@ -327,7 +292,7 @@ internal sealed class ODataResourceTypeContextWithModel : ODataResourceTypeConte
327292
/// <param name="navigationSourceEntityType">The entity type of the navigation source.</param>
328293
/// <param name="expectedResourceType">The expected resource type of the resource set or resource.</param>
329294
internal ODataResourceTypeContextWithModel(IEdmNavigationSource navigationSource, IEdmEntityType navigationSourceEntityType, IEdmStructuredType expectedResourceType)
330-
: base(expectedResourceType, /*throwIfMissingTypeInfo*/false)
295+
: base(expectedResourceType)
331296
{
332297
Debug.Assert(expectedResourceType != null, "expectedResourceType != null");
333298
Debug.Assert(navigationSource != null

src/Microsoft.OData.Core/ODataWriterCore.cs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4101,17 +4101,11 @@ internal ODataResourceTypeContext GetOrCreateTypeContext(bool writingResponse)
41014101
{
41024102
if (this.typeContext == null)
41034103
{
4104-
// For Entity, currently we check the navigation source.
4105-
// For Complex, we don't have navigation source, So we shouldn't check it.
4106-
// If ResourceType is not provided, serialization info or navigation source info should be provided.
4107-
bool throwIfMissingTypeInfo = writingResponse && (this.ResourceType == null || this.ResourceType.TypeKind == EdmTypeKind.Entity);
4108-
41094104
this.typeContext = ODataResourceTypeContext.Create(
41104105
this.serializationInfo,
41114106
this.NavigationSource,
41124107
EdmTypeWriterResolver.Instance.GetElementType(this.NavigationSource),
4113-
this.ResourceType,
4114-
throwIfMissingTypeInfo);
4108+
this.ResourceType);
41154109
}
41164110

41174111
return this.typeContext;
@@ -4270,15 +4264,11 @@ public ODataResourceTypeContext GetOrCreateTypeContext(bool writingResponse)
42704264
{
42714265
IEdmStructuredType expectedResourceType = this.ResourceTypeFromMetadata ?? this.ResourceType;
42724266

4273-
// For entity, we will check the navigation source info
4274-
bool throwIfMissingTypeInfo = writingResponse && (expectedResourceType == null || expectedResourceType.TypeKind == EdmTypeKind.Entity);
4275-
42764267
this.typeContext = ODataResourceTypeContext.Create(
42774268
this.serializationInfo,
42784269
this.NavigationSource,
42794270
EdmTypeWriterResolver.Instance.GetElementType(this.NavigationSource),
4280-
expectedResourceType,
4281-
throwIfMissingTypeInfo);
4271+
expectedResourceType);
42824272
}
42834273

42844274
return this.typeContext;
@@ -4379,8 +4369,7 @@ public ODataResourceTypeContext GetOrCreateTypeContext(bool writingResponse = tr
43794369
this.serializationInfo,
43804370
this.NavigationSource,
43814371
EdmTypeWriterResolver.Instance.GetElementType(this.NavigationSource),
4382-
this.fakeEntityType,
4383-
writingResponse);
4372+
this.fakeEntityType);
43844373
}
43854374

43864375
return this.typeContext;

src/Microsoft.OData.Core/UriParser/Parsers/ODataPathParser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1516,7 +1516,7 @@ private void CreatePropertySegment(ODataPathSegment previous, IEdmProperty prope
15161516
segment.TargetKind = RequestTargetKind.Enum;
15171517
break;
15181518
default:
1519-
Debug.Assert(property.Type.IsPrimitive() || property.Type.IsTypeDefinition(), "must be primitive type or type definition property");
1519+
Debug.Assert(property.Type.IsPrimitive() || property.Type.IsTypeDefinition() || property.Type.IsUntyped(), "must be primitive type or type definition property");
15201520
segment.TargetKind = RequestTargetKind.Primitive;
15211521
break;
15221522
}

0 commit comments

Comments
 (0)