Skip to content

Commit

Permalink
Fix deltas (#1365)
Browse files Browse the repository at this point in the history
* Addition of DeltaFunctionsSample

* Fix Writing Delta Responses
-only write properties in deltaset items that have been set
-support writing additional properties for deleted entities
-nested resource sets that aren't deltasets should be written w/o @delta
-factored out ODataDeletedResourceSerializer
-support writing delta payloads w/out knowing navigation source (i.e., when serializing results from a function)

* Remove DeltaFunctions test project

* Code Clean-up.
Leverage more common functionality in ODataResourceSerializer.

* Apply review feedback
Change ODataDeletedResourceSerializer to derive from ODataResourceSerializer

* Preserve setting of OmitODataPrefix

* Support deleted resources in ODataResourceSerializer, rather than separate serializers
Add tests for deleted resources

* Add ConfigureAwait(false) and test to verify that WriteDeltaDeletedResourceAsync is called if overridden in a derived type.

* Update package references in csproj file
  • Loading branch information
mikepizzo authored Dec 21, 2024
1 parent d0b2f76 commit a852d91
Show file tree
Hide file tree
Showing 21 changed files with 3,172 additions and 194 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ public static Uri GenerateSelfLink(this ResourceContext resourceContext, bool in
throw Error.ArgumentNull(nameof(resourceContext));
}

if (resourceContext.Request == null)
{
return null;
}

IList<ODataPathSegment> idLinkPathSegments = resourceContext.GenerateBaseODataPathSegments();

bool isSameType = resourceContext.StructuredType == resourceContext.NavigationSource?.EntityType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,14 @@ internal static async Task WriteToStreamAsync(
writerSettings.BaseUri = baseAddress;

//use v401 to write delta payloads.
if (serializer.ODataPayloadKind == ODataPayloadKind.Delta)
if (serializer.ODataPayloadKind == ODataPayloadKind.Delta && version < ODataVersion.V401)
{
// Preserve setting of OmitODataPrefix
if (writerSettings.Version.GetValueOrDefault() == ODataVersion.V4)
{
writerSettings.SetOmitODataPrefix(writerSettings.GetOmitODataPrefix(ODataVersion.V4), ODataVersion.V401);
}

writerSettings.Version = ODataVersion.V401;
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System;
using System.Collections;
using System.Diagnostics.Contracts;
using System.Reflection;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.OData.Abstracts;
Expand Down Expand Up @@ -56,10 +57,6 @@ public override async Task WriteObjectAsync(object graph, Type type, ODataMessag
}

IEdmEntitySetBase entitySet = writeContext.NavigationSource as IEdmEntitySetBase;
if (entitySet == null)
{
throw new SerializationException(SRResources.EntitySetMissingDuringSerialization);
}

IEdmTypeReference feedType = writeContext.GetEdmType(graph, type);
Contract.Assert(feedType != null);
Expand Down Expand Up @@ -162,11 +159,19 @@ private async Task WriteDeltaResourceSetAsync(IEnumerable enumerable, IEdmTypeRe
}

lastResource = item;
DeltaItemKind kind = GetDelteItemKind(item);
DeltaItemKind kind = GetDeltaItemKind(item);
switch (kind)
{
case DeltaItemKind.DeletedResource:
await WriteDeltaDeletedResourceAsync(item, writer, writeContext).ConfigureAwait(false);
// hack. if the WriteDeltaDeletedResourceAsync isn't overridden, call the new version
if (WriteDeltaDeletedResourceAsyncIsOverridden())
{
await WriteDeltaDeletedResourceAsync(item, writer, writeContext).ConfigureAwait(false);
}
else
{
await WriteDeletedResourceAsync(item, elementType, writer, writeContext).ConfigureAwait(false);
}
break;
case DeltaItemKind.DeltaDeletedLink:
await WriteDeltaDeletedLinkAsync(item, writer, writeContext).ConfigureAwait(false);
Expand Down Expand Up @@ -212,7 +217,7 @@ await entrySerializer.WriteDeltaObjectInlineAsync(item, elementType, writer, wri
/// <param name="writeContext">The serializer context.</param>
/// <returns>The function that generates the NextLink from an object.</returns>
/// <returns></returns>
internal static Func<object, Uri> GetNextLinkGenerator(ODataDeltaResourceSet deltaResourceSet, IEnumerable enumerable, ODataSerializerContext writeContext)
internal static Func<object, Uri> GetNextLinkGenerator(ODataResourceSetBase deltaResourceSet, IEnumerable enumerable, ODataSerializerContext writeContext)
{
return ODataResourceSetSerializer.GetNextLinkGenerator(deltaResourceSet, enumerable, writeContext);
}
Expand Down Expand Up @@ -266,6 +271,8 @@ public virtual ODataDeltaResourceSet CreateODataDeltaResourceSet(IEnumerable fee
/// <param name="value">The object to be written.</param>
/// <param name="writer">The <see cref="ODataDeltaWriter" /> to be used for writing.</param>
/// <param name="writeContext">The <see cref="ODataSerializerContext"/>.</param>
[Obsolete("WriteDeltaDeletedResourceAsync(object, ODataWriter, ODataSerializerContext) is Deprecated and will be removed in the next version." +
"Please use WriteDeletedResourceAsync(object, IEdmEntityTypeReference, ODataWriter, ODataSerializerContext)")]
public virtual async Task WriteDeltaDeletedResourceAsync(object value, ODataWriter writer, ODataSerializerContext writeContext)
{
if (writer == null)
Expand Down Expand Up @@ -304,6 +311,31 @@ public virtual async Task WriteDeltaDeletedResourceAsync(object value, ODataWrit
}
}

/// <summary>
/// Writes the given deltaDeletedEntry specified by the parameter graph as a part of an existing OData message using the given
/// messageWriter and the writeContext.
/// </summary>
/// <param name="value">The object to be written.</param>
/// <param name="expectedType">The expected type of the deleted resource.</param>
/// <param name="writer">The <see cref="ODataDeltaWriter" /> to be used for writing.</param>
/// <param name="writeContext">The <see cref="ODataSerializerContext"/>.</param>
public virtual async Task WriteDeletedResourceAsync(object value, IEdmStructuredTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
{
if (writer == null)
{
throw Error.ArgumentNull(nameof(writer));
}

IODataEdmTypeSerializer deletedResourceSerializer = SerializerProvider.GetEdmTypeSerializer(expectedType);
if (deletedResourceSerializer == null)
{
throw new SerializationException(
Error.Format(SRResources.TypeCannotBeSerialized, expectedType.FullName()));
}

await deletedResourceSerializer.WriteObjectInlineAsync(value, expectedType, writer, writeContext);
}

/// <summary>
/// Writes the given deltaDeletedLink specified by the parameter graph as a part of an existing OData message using the given
/// messageWriter and the writeContext.
Expand Down Expand Up @@ -382,7 +414,7 @@ public virtual async Task WriteDeltaLinkAsync(object value, ODataWriter writer,
}
}

internal DeltaItemKind GetDelteItemKind(object item)
internal DeltaItemKind GetDeltaItemKind(object item)
{
IEdmChangedObject edmChangedObject = item as IEdmChangedObject;
if (edmChangedObject != null)
Expand Down Expand Up @@ -414,4 +446,17 @@ private static IEdmStructuredTypeReference GetResourceType(IEdmTypeReference fee
string message = Error.Format(SRResources.CannotWriteType, typeof(ODataDeltaResourceSetSerializer).Name, feedType.FullName());
throw new SerializationException(message);
}

// Discover whether or not WriteDeltaDeletedResourceAsync is overridden in a derived class.
// WriteDeltaDeletedResourceAsync is deprecated in favor of WriteDeletedResourceAsync, but
// to avoid breaking changes, this retains the behavior of calling a custom
// WriteDeltaDeletedResourceAsync method for the case that the service has overriden that
// method with a custom implementation. In the next breaking change, WriteDeltaDeletedResourceAsync
// should be removed, and this private method can be deleted.
private bool WriteDeltaDeletedResourceAsyncIsOverridden()
{
MethodInfo method = GetType().GetMethod("WriteDeltaDeletedResourceAsync", new Type[] { typeof(object), typeof(ODataWriter), typeof(ODataSerializerContext) });
Contract.Assert(method != null, "WriteDeltaDeletedResourceAsync is not defined.");
return method.DeclaringType != typeof(ODataDeltaResourceSetSerializer);
}
}
Loading

0 comments on commit a852d91

Please sign in to comment.