Skip to content

Commit

Permalink
Support for Media Link entities with no tracking (#1738)
Browse files Browse the repository at this point in the history
* Enable Stream entries to work in non-tracking mode.
  • Loading branch information
marabooy authored Jun 10, 2020
1 parent 0c0d585 commit 5464035
Show file tree
Hide file tree
Showing 9 changed files with 558 additions and 46 deletions.
7 changes: 6 additions & 1 deletion src/Microsoft.OData.Client/BaseEntityType.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// <copyright file="BaseEntityType.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
Expand All @@ -13,5 +13,10 @@ public class BaseEntityType
{
/// <summary>DataServiceContext for query provider</summary>
protected internal DataServiceContext Context { get; set; }

/// <summary>
/// Entity descriptor containing entity stream links
/// </summary>
protected internal EntityDescriptor EntityDescriptor { get; set; }
}
}
41 changes: 37 additions & 4 deletions src/Microsoft.OData.Client/DataServiceContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1266,8 +1266,40 @@ public virtual QueryOperationResponse<T> LoadProperty<T>(object entity, string p
public virtual Uri GetReadStreamUri(object entity)
{
Util.CheckArgumentNull(entity, "entity");
EntityDescriptor box = this.entityTracker.GetEntityDescriptor(entity);
return box.ReadStreamUri;
EntityDescriptor descriptor = this.GetEntityDescriptorForStreamEntity(entity);
return descriptor.ReadStreamUri;
}

/// <summary>
/// This function is used to get the entity descriptor that is to be checked when trying to resolve streams.
/// For other use cases use the <see cref="GetEntityDescriptor"/> function
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
private EntityDescriptor GetEntityDescriptorForStreamEntity(object entity)
{
EntityDescriptor descriptor;
if (MergeOption == MergeOption.NoTracking)
{
BaseEntityType baseEntity = entity as BaseEntityType;
if (baseEntity == null)
{
throw Error.InvalidOperation(Strings.Context_EntityMediaLinksNotTrackedInEntity);
}

descriptor = baseEntity.EntityDescriptor;

if (descriptor == null)
{
throw Error.InvalidOperation(Strings.Context_EntityInNonTrackedContextLacksMediaLinks);
}
}
else
{
descriptor = this.entityTracker.GetEntityDescriptor(entity);
}

return descriptor;
}

/// <summary>Gets the URI that is used to return a named binary data stream.</summary>
Expand All @@ -1282,7 +1314,7 @@ public virtual Uri GetReadStreamUri(object entity, string name)
Util.CheckArgumentNull(entity, "entity");
Util.CheckArgumentNullAndEmpty(name, "name");
this.EnsureMinimumProtocolVersionV3();
EntityDescriptor entityDescriptor = this.entityTracker.GetEntityDescriptor(entity);
EntityDescriptor entityDescriptor = this.GetEntityDescriptorForStreamEntity(entity);
StreamDescriptor namedStreamInfo;
if (entityDescriptor.TryGetNamedStreamInfo(name, out namedStreamInfo))
{
Expand Down Expand Up @@ -3320,8 +3352,9 @@ private GetReadStreamResult CreateGetReadStreamResult(
Util.CheckArgumentNull(entity, "entity");
Util.CheckArgumentNull(args, "args");

EntityDescriptor entityDescriptor = this.entityTracker.GetEntityDescriptor(entity);
EntityDescriptor entityDescriptor = this.GetEntityDescriptorForStreamEntity(entity);
StreamDescriptor streamDescriptor;

Uri requestUri;
Version version;
if (name == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//---------------------------------------------------------------------
//---------------------------------------------------------------------
// <copyright file="EntryValueMaterializationPolicy.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
// </copyright>
Expand All @@ -15,8 +15,10 @@ namespace Microsoft.OData.Client.Materialization
using Microsoft.OData;
using Microsoft.OData.Client;
using Microsoft.OData.Client.Metadata;
using Microsoft.OData.Edm;
using DSClient = Microsoft.OData.Client;


/// <summary>
/// Used to materialize entities from an <see cref="ODataResource"/> to an object.
/// </summary>
Expand Down Expand Up @@ -680,6 +682,18 @@ private void MaterializeResolvedEntry(MaterializerEntry entry, bool includeLinks
if (entity != null)
{
entity.Context = this.EntityTrackingAdapter.Context;

if (!entry.IsTracking)
{
int? streamDescriptorsCount = entry.EntityDescriptor.StreamDescriptors?.Count;
IEdmEntityType entityType =
this.EntityTrackingAdapter.Model.FindDeclaredType(entry.Entry.TypeName) as IEdmEntityType;

if (streamDescriptorsCount > 0 || entityType?.HasStream == true)
{
entity.EntityDescriptor = entry.EntityDescriptor;
}
}
}

this.MaterializerContext.ResponsePipeline.FireEndEntryEvents(entry);
Expand Down
39 changes: 17 additions & 22 deletions src/Microsoft.OData.Client/Materialization/MaterializerEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,37 +314,32 @@ public void UpdateEntityDescriptor()
}
}

if (this.IsTracking)
// We need this to be populated as well for entities that may contain this
if (this.entry.MediaResource != null)
{
if (this.Id == null)
if (this.entry.MediaResource.ReadLink != null)
{
this.EntityDescriptor.ReadStreamUri = this.entry.MediaResource.ReadLink;
}

if (this.entry.MediaResource.EditLink != null)
{
// TODO: Remove these lines since complex type doesn't have Id.
// throw DSClient.Error.InvalidOperation(DSClient.Strings.Deserialize_MissingIdElement);
this.EntityDescriptor.EditStreamUri = this.entry.MediaResource.EditLink;
}

if (this.entry.MediaResource.ETag != null)
{
this.EntityDescriptor.StreamETag = this.entry.MediaResource.ETag;
}
}

if (this.IsTracking)
{
this.EntityDescriptor.Identity = this.entry.Id;
this.EntityDescriptor.EditLink = this.entry.EditLink;
this.EntityDescriptor.SelfLink = this.entry.ReadLink;
this.EntityDescriptor.ETag = this.entry.ETag;

if (this.entry.MediaResource != null)
{
if (this.entry.MediaResource.ReadLink != null)
{
this.EntityDescriptor.ReadStreamUri = this.entry.MediaResource.ReadLink;
}

if (this.entry.MediaResource.EditLink != null)
{
this.EntityDescriptor.EditStreamUri = this.entry.MediaResource.EditLink;
}

if (this.entry.MediaResource.ETag != null)
{
this.EntityDescriptor.StreamETag = this.entry.MediaResource.ETag;
}
}

if (this.entry.Functions != null)
{
foreach (ODataFunction function in this.entry.Functions)
Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.OData.Client/Microsoft.OData.Client.Common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ Context_CannotChangeStateIfAdded=An entity in the 'Added' state cannot be change
Context_OnMessageCreatingReturningNull=DataServiceContext.Configurations.RequestPipeline.OnMessageCreating property must not return a null value. Please return a non-null value for this property.
Context_SendingRequest_InvalidWhenUsingOnMessageCreating=SendingRequest cannot be used in combination with the DataServiceContext.Configurations.RequestPipeline.OnMessageCreating property. Please use SendingRequest2 with DataServiceContext.Configurations.RequestPipeline.OnMessageCreating property instead.
Context_MustBeUsedWith='{0}' must be used with '{1}'.
Context_EntityMediaLinksNotTrackedInEntity=The context is currently in no tracking mode, in order to use streams make sure your entities extend BaseEntityType and query the Item again from the server to populate the read link or enable tracking.
Context_EntityInNonTrackedContextLacksMediaLinks=The context is in non tracking mode, The entity does not seem to have the media links populated properly. Please verify server response is correct and that the entity extends BaseEntityType.

DataServiceClientFormat_LoadServiceModelRequired=When you call the UseJson method without a parameter, you must use the LoadServiceModel property to provide a valid IEdmModel instance.
DataServiceClientFormat_ValidServiceModelRequiredForJson=To use the JSON format, you must first call DataServiceContext.Format.UseJson() and supply a valid service model.
Expand Down
19 changes: 2 additions & 17 deletions src/Microsoft.OData.Client/Microsoft.OData.Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
using System.Reflection;
using System.Resources;
using System.Threading;
#if !PORTABLELIB
using System.Security.Permissions;
#endif

namespace Microsoft.OData.Client
{
Expand Down Expand Up @@ -92,6 +89,8 @@ internal sealed class TextRes {
internal const string Context_OnMessageCreatingReturningNull = "Context_OnMessageCreatingReturningNull";
internal const string Context_SendingRequest_InvalidWhenUsingOnMessageCreating = "Context_SendingRequest_InvalidWhenUsingOnMessageCreating";
internal const string Context_MustBeUsedWith = "Context_MustBeUsedWith";
internal const string Context_EntityMediaLinksNotTrackedInEntity = "Context_EntityMediaLinksNotTrackedInEntity";
internal const string Context_EntityInNonTrackedContextLacksMediaLinks = "Context_EntityInNonTrackedContextLacksMediaLinks";
internal const string DataServiceClientFormat_LoadServiceModelRequired = "DataServiceClientFormat_LoadServiceModelRequired";
internal const string DataServiceClientFormat_ValidServiceModelRequiredForJson = "DataServiceClientFormat_ValidServiceModelRequiredForJson";
internal const string Collection_NullCollectionReference = "Collection_NullCollectionReference";
Expand Down Expand Up @@ -276,11 +275,7 @@ internal sealed class TextRes {

internal TextRes()
{
#if !PORTABLELIB
resources = new System.Resources.ResourceManager("Microsoft.OData.Client", this.GetType().Assembly);
#else
resources = new System.Resources.ResourceManager("Microsoft.OData.Client", this.GetType().GetTypeInfo().Assembly);
#endif
}

private static TextRes GetLoader()
Expand Down Expand Up @@ -352,15 +347,5 @@ public static string GetString(string name, out bool usedFallback)
usedFallback = false;
return GetString(name);
}

#if !PORTABLELIB
public static object GetObject(string name)
{
TextRes sys = GetLoader();
if (sys == null)
return null;
return sys.resources.GetObject(name, TextRes.Culture);
}
#endif
}
}
18 changes: 18 additions & 0 deletions src/Microsoft.OData.Client/Parameterized.Microsoft.OData.Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,24 @@ internal static string Context_MustBeUsedWith(object p0, object p1) {
return Microsoft.OData.Client.TextRes.GetString(Microsoft.OData.Client.TextRes.Context_MustBeUsedWith, p0, p1);
}

/// <summary>
/// A string like "The context is currently in no tracking mode, in order to use streams make sure your entities extend BaseEntityType and query the Item again from the server to populate the read link or enable tracking."
/// </summary>
internal static string Context_EntityMediaLinksNotTrackedInEntity {
get {
return Microsoft.OData.Client.TextRes.GetString(Microsoft.OData.Client.TextRes.Context_EntityMediaLinksNotTrackedInEntity);
}
}

/// <summary>
/// A string like "The context is in non tracking mode, The entity does not seem to have the media links populated properly. Please verify server response is correct and that the entity extends BaseEntityType."
/// </summary>
internal static string Context_EntityInNonTrackedContextLacksMediaLinks {
get {
return Microsoft.OData.Client.TextRes.GetString(Microsoft.OData.Client.TextRes.Context_EntityInNonTrackedContextLacksMediaLinks);
}
}

/// <summary>
/// A string like "When you call the UseJson method without a parameter, you must use the LoadServiceModel property to provide a valid IEdmModel instance."
/// </summary>
Expand Down
Loading

0 comments on commit 5464035

Please sign in to comment.