Example #1
0
 /// <summary>
 /// Creates a StreamDescriptor class with the given name and other information
 /// </summary>
 /// <param name="name">name of the stream.</param>
 /// <param name="entityDescriptor">instance of entity descriptor that contains this stream.</param>
 internal StreamDescriptor(string name, EntityDescriptor entityDescriptor) : base(EntityStates.Unchanged)
 {
     Debug.Assert(!String.IsNullOrEmpty(name), "!String.IsNullOrEmpty(name)");
     Debug.Assert(entityDescriptor != null, "entityDescriptor != null");
     this.streamLink = new DataServiceStreamLink(name);
     this.entityDescriptor = entityDescriptor;
 }
Example #2
0
 /// <summary>
 /// Creates a StreamDescriptor class for the default stream (MR) associated with an entity.
 /// </summary>
 /// <param name="entityDescriptor">instance of entity descriptor that contains this stream.</param>
 internal StreamDescriptor(EntityDescriptor entityDescriptor)
     : base(EntityStates.Unchanged)
 {
     Debug.Assert(entityDescriptor != null, "entityDescriptor != null");
     this.streamLink = new DataServiceStreamLink(null);
     this.entityDescriptor = entityDescriptor;
 }
        /// <summary>
        /// Instantiates a new Serializer class and calls WriteEntry method on it.
        /// </summary>
        /// <param name="dataServiceContext"></param>
        /// <returns></returns>
        private static Person SetupSerializerAndCallWriteEntry(DataServiceContext dataServiceContext)
        {
            Person person = new Person();
            Address address = new Address();
            Car car1 = new Car();
            person.Cars.Add(car1);
            person.HomeAddress = address;

            dataServiceContext.AttachTo("Cars", car1);
            dataServiceContext.AttachTo("Addresses", address);

            var requestInfo = new RequestInfo(dataServiceContext);
            var serializer = new Serializer(requestInfo);
            var headers = new HeaderCollection();
            var clientModel = new ClientEdmModel(ODataProtocolVersion.V4);
            var entityDescriptor = new EntityDescriptor(clientModel);
            entityDescriptor.State = EntityStates.Added;
            entityDescriptor.Entity = person;
            var requestMessageArgs = new BuildingRequestEventArgs("POST", new Uri("http://www.foo.com/Northwind"), headers, entityDescriptor, HttpStack.Auto);
            var linkDescriptors = new LinkDescriptor[] { new LinkDescriptor(person, "Cars", car1, clientModel), new LinkDescriptor(person, "HomeAddress", address, clientModel) };
            var odataRequestMessageWrapper = ODataRequestMessageWrapper.CreateRequestMessageWrapper(requestMessageArgs, requestInfo);

            serializer.WriteEntry(entityDescriptor, linkDescriptors, odataRequestMessageWrapper);
            return person;
        }
Example #4
0
        /// <summary>
        /// Creates a new instance of MaterializerEntry using the given entity descriptor for LoadProperty.
        /// </summary>
        /// <param name="entityDescriptor">Entity descriptor.</param>
        /// <param name="format">OData Format.</param>
        /// <param name="isTracking">Whether this entity is being tracked.</param>
        /// <remarks>Use this constructor only for LoadProperty scenario.</remarks>
        private MaterializerEntry(EntityDescriptor entityDescriptor, ODataFormat format, bool isTracking)
        {
            this.entityDescriptor = entityDescriptor;
            this.Format = format;
#pragma warning disable 618
            this.isAtomOrTracking = isTracking || this.Format == ODataFormat.Atom;
#pragma warning restore 618
            this.SetFlagValue(EntryFlags.ShouldUpdateFromPayload | EntryFlags.EntityHasBeenResolved | EntryFlags.ForLoadProperty, true);
        }
Example #5
0
        public void Init()
        {
            this.clientEdmModel = new ClientEdmModel(ODataProtocolVersion.V4);
            this.entityDescriptor = new EntityDescriptor(this.clientEdmModel) { Entity = new Customer() };

            var serverType = new EdmEntityType("FQ.NS", "MyServerType");
            serverType.AddUnidirectionalNavigation(new EdmNavigationPropertyInfo { Name = "Navigation", Target = serverType, TargetMultiplicity = EdmMultiplicity.ZeroOrOne });
            this.serverTypeName = ((IEdmSchemaElement)serverType).FullName();
            var serverContainer = new EdmEntityContainer("FQ.NS", "MyContainer");
            this.serverEntitySet = serverContainer.AddEntitySet("MyEntitySet", serverType);

            var serverModel = new EdmModel();
            serverModel.AddElement(serverType);
            serverModel.AddElement(serverContainer);

            this.ctx = new DataServiceContext(new Uri("http://temp.org/"), ODataProtocolVersion.V4, this.clientEdmModel);
            this.ctx.Format.UseJson(serverModel);
            this.testSubject = new RequestInfo(ctx);
        }
Example #6
0
        /// <summary>
        /// Creates a new instance of MaterializerEntry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <param name="format">The format the entry was read in.</param>
        /// <param name="isTracking">True if the contents of the entry will be tracked in the context, otherwise False.</param>
        /// <param name="model">The client model.</param>
        private MaterializerEntry(ODataEntry entry, ODataFormat format, bool isTracking, ClientEdmModel model)
        {
            Debug.Assert(entry != null, "entry != null");

            this.entry = entry;
            this.Format = format;
            this.entityDescriptor = new EntityDescriptor(model);
#pragma warning disable 618
            this.isAtomOrTracking = isTracking || this.Format == ODataFormat.Atom;
#pragma warning restore 618
            string serverTypeName = this.Entry.TypeName;
            SerializationTypeNameAnnotation serializationTypeNameAnnotation = entry.GetAnnotation<SerializationTypeNameAnnotation>();
            if (serializationTypeNameAnnotation != null)
            {
                // If the annotation has a value use it. Otherwise, in JSON-Light, the types can be inferred from the
                // context URI even if they are not present on the wire, so just use the type name from the entry.
                if (serializationTypeNameAnnotation.TypeName != null || this.Format != ODataFormat.Json)
                {
                    serverTypeName = serializationTypeNameAnnotation.TypeName;
                }
            }

            this.entityDescriptor.ServerTypeName = serverTypeName;
        }
Example #7
0
        /// <summary>
        /// Attach entity into the context in the Unchanged state.
        /// </summary>
        /// <param name="entityDescriptorFromMaterializer">entity descriptor from the response</param>
        /// <param name="failIfDuplicated">fail for public api else change existing relationship to unchanged</param>
        /// <remarks>Caller should validate descriptor instance.</remarks>
        /// <returns>The attached descriptor, if one already exists in the context and failIfDuplicated is set to false, then the existing instance is returned</returns>
        /// <exception cref="InvalidOperationException">if entity is already being tracked by the context</exception>
        /// <exception cref="InvalidOperationException">if identity is pointing to another entity</exception>
        internal override EntityDescriptor InternalAttachEntityDescriptor(EntityDescriptor entityDescriptorFromMaterializer, bool failIfDuplicated)
        {
            Debug.Assert((null != entityDescriptorFromMaterializer.Identity), "must have identity");
            Debug.Assert(null != entityDescriptorFromMaterializer.Entity && ClientTypeUtil.TypeIsEntity(entityDescriptorFromMaterializer.Entity.GetType(), this.model), "must be entity type to attach");

            this.EnsureIdentityToResource();

            EntityDescriptor trackedEntityDescriptor;
            this.entityDescriptors.TryGetValue(entityDescriptorFromMaterializer.Entity, out trackedEntityDescriptor);

            EntityDescriptor existing;
            this.identityToDescriptor.TryGetValue(entityDescriptorFromMaterializer.Identity, out existing);

            // identity existing & pointing to something else
            if (failIfDuplicated && (null != trackedEntityDescriptor))
            {
                throw Error.InvalidOperation(Strings.Context_EntityAlreadyContained);
            }
            else if (trackedEntityDescriptor != existing)
            {
                throw Error.InvalidOperation(Strings.Context_DifferentEntityAlreadyContained);
            }
            else if (null == trackedEntityDescriptor)
            {
                trackedEntityDescriptor = entityDescriptorFromMaterializer;

                // if resource doesn't exist...
                this.IncrementChange(entityDescriptorFromMaterializer);
                this.entityDescriptors.Add(entityDescriptorFromMaterializer.Entity, entityDescriptorFromMaterializer);
                this.identityToDescriptor.Add(entityDescriptorFromMaterializer.Identity, entityDescriptorFromMaterializer);
            }

            // DEVNOTE(pqian):
            // we used to mark the descriptor as Unchanged
            // but it's now up to the caller to do that
            return trackedEntityDescriptor;
        }
Example #8
0
 internal void DetachResourceIdentity(EntityDescriptor resource)
 {
     EntityDescriptor existing = null;
     if ((null != resource.Identity) &&
         this.identityToDescriptor.TryGetValue(resource.Identity, out existing) &&
         Object.ReferenceEquals(existing, resource))
     {
         bool removed = this.identityToDescriptor.Remove(resource.Identity);
         Debug.Assert(removed, "should have removed existing identity");
     }
 }
Example #9
0
 /// <summary>
 /// Adds the given entity descriptors to the list of the tracked entity descriptors.
 /// </summary>
 /// <param name="descriptor">entity descriptor instance to be added.</param>
 internal void AddEntityDescriptor(EntityDescriptor descriptor)
 {
     try
     {
         this.entityDescriptors.Add(descriptor.Entity, descriptor);
     }
     catch (ArgumentException)
     {
         throw Error.InvalidOperation(Strings.Context_EntityAlreadyContained);
     }
 }
Example #10
0
        /// <summary>
        /// Write the entry element.
        /// </summary>
        /// <param name="entityDescriptor">The entity.</param>
        /// <param name="relatedLinks">Collection of links related to the entity.</param>
        /// <param name="requestMessage">The OData request message.</param>
        internal void WriteEntry(EntityDescriptor entityDescriptor, IEnumerable<LinkDescriptor> relatedLinks, ODataRequestMessageWrapper requestMessage)
        {
            ClientEdmModel model = this.requestInfo.Model;
            ClientTypeAnnotation entityType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));
            using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, false /*isParameterPayload*/))
            {
                ODataWriterWrapper entryWriter = ODataWriterWrapper.CreateForEntry(messageWriter, this.requestInfo.Configurations.RequestPipeline);

                // Get the server type name using the type resolver or from the entity descriptor
                string serverTypeName = this.requestInfo.GetServerTypeName(entityDescriptor);

                var entry = CreateODataEntry(entityDescriptor, serverTypeName, entityType, this.requestInfo.Format);
                if (serverTypeName == null)
                {
                    serverTypeName = this.requestInfo.InferServerTypeNameFromServerModel(entityDescriptor);
                }

                IEnumerable<ClientPropertyAnnotation> properties;
                if ((!Util.IsFlagSet(this.options, SaveChangesOptions.ReplaceOnUpdate) &&
                    entityDescriptor.State == EntityStates.Modified &&
                    entityDescriptor.PropertiesToSerialize.Any()) ||
                    (Util.IsFlagSet(this.options, SaveChangesOptions.PostOnlySetProperties) &&
                    entityDescriptor.State == EntityStates.Added))
                {
                    properties = entityType.PropertiesToSerialize().Where(prop => entityDescriptor.PropertiesToSerialize.Contains(prop.PropertyName));
                }
                else
                {
                    properties = entityType.PropertiesToSerialize();
                }

                entry.Properties = this.propertyConverter.PopulateProperties(entityDescriptor.Entity, serverTypeName, properties);

                entryWriter.WriteStart(entry, entityDescriptor.Entity);

                if (EntityStates.Added == entityDescriptor.State)
                {
                    this.WriteNavigationLink(entityDescriptor, relatedLinks, entryWriter);
                }

                entryWriter.WriteEnd(entry, entityDescriptor.Entity);
            }
        }
Example #11
0
        public void RequestInfoShouldCreateTunneledPatchRequestMessagePostMethodAndPatchInHttpXMethodHeader()
        {
            bool previousPostTunnelingValue = ctx.UsePostTunneling;
            ctx.UsePostTunneling = true;
            HeaderCollection headersCollection = new HeaderCollection();
            var descriptor = new EntityDescriptor(this.clientEdmModel) { ServerTypeName = this.serverTypeName, Entity = new Customer() };
            var buildingRequestArgs = new BuildingRequestEventArgs("PATCH", new Uri("http://localhost/fakeService.svc/"), headersCollection, descriptor, HttpStack.Auto);

            var requestMessage = (HttpWebRequestMessage)testSubject.CreateRequestMessage(buildingRequestArgs);

            requestMessage.GetHeader(XmlConstants.HttpXMethod).Should().Be("PATCH");
            requestMessage.Method.Should().Be("PATCH");
            requestMessage.HttpWebRequest.Method.Should().Be("POST");

            // undoing change so this is applicable only for this test.
            ctx.UsePostTunneling = previousPostTunnelingValue;
        }
Example #12
0
 /// <summary>
 /// Returns the instance of LoadPropertyResponseInfo class, which provides information for LoadProperty response handling.
 /// </summary>
 /// <param name="mergeOption">Merge option to use for conflict handling.</param>
 /// <param name="entityDescriptor">Entity whose property is being loaded.</param>
 /// <param name="property">Property which is being loaded.</param>
 /// <returns>Instance of the LoadPropertyResponseInfo class.</returns>
 internal ResponseInfo GetDeserializationInfoForLoadProperty(MergeOption? mergeOption, EntityDescriptor entityDescriptor, ClientPropertyAnnotation property)
 {
     return new LoadPropertyResponseInfo(
         this,
         mergeOption.HasValue ? mergeOption.Value : this.Context.MergeOption,
         entityDescriptor,
         property);
 }
Example #13
0
        /// <summary>
        /// Generate a request for the given entity.
        /// </summary>
        /// <param name="entityDescriptor">Instance of EntityDescriptor.</param>
        /// <param name="requestMessage">Instance of IODataRequestMessage to be used to generate the payload.</param>
        /// <returns>True if the payload was generated, otherwise false.</returns>
        private bool CreateRequestData(EntityDescriptor entityDescriptor, ODataRequestMessageWrapper requestMessage)
        {
            Debug.Assert(null != entityDescriptor, "null entityDescriptor");
            bool generateRequestPayload = false;
            switch (entityDescriptor.State)
            {
                case EntityStates.Deleted:
                    break;
                case EntityStates.Modified:
                case EntityStates.Added:
                    generateRequestPayload = true;
                    break;
                default:
                    Error.ThrowInternalError(InternalError.UnvalidatedEntityState);
                    break;
            }

            if (generateRequestPayload)
            {
                Debug.Assert(this.SerializerInstance != null, "this.SerializerInstance != null");
                this.SerializerInstance.WriteEntry(entityDescriptor, this.RelatedLinks(entityDescriptor), requestMessage);
            }

            return generateRequestPayload;
        }
Example #14
0
        private static Uri AppendTargetEntityKeyIfNeeded(Uri linkUri, LinkDescriptor binding, EntityDescriptor targetResource)
        {
            // To delete from a collection, we need to append the key.
            // For example: if the navigation property name is "Purchases" and the resource type is Order with key '1', then this method will generate 'baseuri/Purchases(1)'
            if (!binding.IsSourcePropertyCollection || EntityStates.Deleted != binding.State)
            {
                return linkUri;
            }

            Debug.Assert(targetResource != null, "targetResource != null");
            StringBuilder builder = new StringBuilder();
            builder.Append(UriUtil.UriToString(linkUri));
            builder.Append(UriHelper.QUESTIONMARK + XmlConstants.HttpQueryStringId + UriHelper.EQUALSSIGN + targetResource.Identity);
            return UriUtil.CreateUri(builder.ToString(), UriKind.RelativeOrAbsolute);
        }
 /// <summary>
 /// clears all the changes - like closes the save stream, clears the transient entity descriptor.
 /// This method is called when the client is done with sending all the pending requests.
 /// </summary>
 internal override void ClearChanges()
 {
     this.transientEntityDescriptor = null;
     this.CloseSaveStream();
 }
Example #16
0
        /// <summary>Handle changeset response for the given entity descriptor.</summary>
        /// <param name="entityDescriptor">entity descriptor whose response is getting handled.</param>
        /// <param name="etag">ETag header value from the server response (or null if no etag or if there is an actual response)</param>
        private void HandleResponsePost(EntityDescriptor entityDescriptor, string etag)
        {
            try
            {
                if (EntityStates.Added != entityDescriptor.State && EntityStates.Added != entityDescriptor.StreamState)
                {
                    Error.ThrowBatchUnexpectedContent(InternalError.EntityNotAddedState);
                }

                if (this.ProcessResponsePayload)
                {
                    this.MaterializeResponse(entityDescriptor, this.CreateResponseInfo(entityDescriptor), etag);
                }
                else
                {
                    entityDescriptor.ETag = etag;
                    entityDescriptor.State = EntityStates.Unchanged;
                    entityDescriptor.PropertiesToSerialize.Clear();
                }

                if (entityDescriptor.StreamState != EntityStates.Added)
                {
                    // For MR - entityDescriptor.State is merged, we don't need to do link folding since MR will never fold links.
                    foreach (LinkDescriptor end in this.RelatedLinks(entityDescriptor))
                    {
                        Debug.Assert(0 != end.SaveResultWasProcessed, "link should have been saved with the enty");

                        // Since we allow link folding on collection properties also, we need to check if the link
                        // was in added state also, and make sure we put that link in unchanged state.
                        if (Util.IncludeLinkState(end.SaveResultWasProcessed) || end.SaveResultWasProcessed == EntityStates.Added)
                        {
                            HandleResponsePost(end);
                        }
                    }
                }
            }
            finally
            {
                if (entityDescriptor.StreamState == EntityStates.Added)
                {
                    // The materializer will always set the entity state to Unchanged.  We just processed Post MR, we
                    // need to restore the entity state to Modified to process the Put MLE.
                    Debug.Assert(entityDescriptor.State == EntityStates.Unchanged, "The materializer should always set the entity state to Unchanged.");
                    entityDescriptor.State = EntityStates.Modified;

                    // Need to clear the stream state so the next iteration we will always process the Put MLE operation.
                    entityDescriptor.StreamState = EntityStates.Unchanged;
                }
            }
        }
Example #17
0
        /// <summary>
        /// Infers the server type name for the entity tracked in the given descriptor based on the server model.
        /// </summary>
        /// <param name="descriptor">The descriptor containing the entity to get the type name for.</param>
        /// <returns>The type name or null if it could not be inferred.</returns>
        internal string InferServerTypeNameFromServerModel(EntityDescriptor descriptor)
        {
            Debug.Assert(descriptor != null, "descriptor != null");
            Debug.Assert(descriptor.ServerTypeName == null, "Should not be called if the server type name is already known.");

            if (descriptor.EntitySetName != null)
            {
                string serverTypeName;
                if (this.TypeResolver.TryResolveEntitySetBaseTypeName(descriptor.EntitySetName, out serverTypeName))
                {
                    return serverTypeName;
                }
            }
            else if (descriptor.IsDeepInsert)
            {
                string parentServerTypeName = this.GetServerTypeName(descriptor.ParentForInsert);
                if (parentServerTypeName == null)
                {
                    parentServerTypeName = this.InferServerTypeNameFromServerModel(descriptor.ParentForInsert);
                }

                string serverTypeName;
                if (this.TypeResolver.TryResolveNavigationTargetTypeName(parentServerTypeName, descriptor.ParentPropertyForInsert, out serverTypeName))
                {
                    return serverTypeName;
                }
            }

            return null;
        }
Example #18
0
        /// <summary>
        /// Materialize the response payload.
        /// </summary>
        /// <param name="entityDescriptor">entity descriptor whose response is getting materialized.</param>
        /// <param name="responseInfo">information about the response to be materialized.</param>
        /// <param name="etag">etag value, if specified in the response header.</param>
        private void MaterializeResponse(EntityDescriptor entityDescriptor, ResponseInfo responseInfo, string etag)
        {
            using (MaterializeAtom materializer = this.GetMaterializer(entityDescriptor, responseInfo))
            {
                materializer.SetInsertingObject(entityDescriptor.Entity);

                object materializedEntity = null;
                foreach (object x in materializer)
                {
                    Debug.Assert(materializedEntity == null, "entity == null");
                    if (materializedEntity != null)
                    {
                        Error.ThrowInternalError(InternalError.MaterializerReturningMoreThanOneEntity);
                    }

                    materializedEntity = x;
                }

                Debug.Assert(null != entityDescriptor.GetLatestIdentity(), "updated inserted should always gain an identity");
                Debug.Assert(materializedEntity == entityDescriptor.Entity, "x == entityDescriptor.Entity, should have same object generated by response");
                Debug.Assert(EntityStates.Unchanged == entityDescriptor.State, "should have moved out of insert");
                Debug.Assert(this.RequestInfo.EntityTracker.TryGetEntityDescriptor(entityDescriptor.GetLatestIdentity()) != null, "should have identity tracked");

                // If there was no etag specified in the payload, then we need to set the etag from the header
                if (entityDescriptor.GetLatestETag() == null)
                {
                    entityDescriptor.ETag = etag;
                }
            }
        }
Example #19
0
        /// <summary>
        /// Get the materializer to process the response.
        /// </summary>
        /// <param name="entityDescriptor">entity descriptor whose response is getting materialized.</param>
        /// <param name="responseInfo">information about the response to be materialized.</param>
        /// <returns>an instance of MaterializeAtom, that can be used to materialize the response.</returns>
        /// <remarks>
        /// This can only be called from inside the HandleBatchResponse or during enumeration of the responses.
        /// This is used when processing responses for update operations.
        /// </remarks>
        protected override MaterializeAtom GetMaterializer(EntityDescriptor entityDescriptor, ResponseInfo responseInfo)
        {
            // check if the batch stream is empty or not
            Debug.Assert(this.currentOperationResponse != null, "There must be an active operation response for this method to work correctly.");
            Debug.Assert(!this.currentOperationResponse.HasEmptyContent, "We should not get here if the response is empty.");

            // Since this is used for processing responses to update operations there are no projections to apply.
            QueryComponents queryComponents = new QueryComponents(
                /*uri*/ null,
                Util.ODataVersionEmpty,
                entityDescriptor.Entity.GetType(),
                /*projection*/ null,
                /*normalizerRewrites*/ null);
            return new MaterializeAtom(
                responseInfo,
                queryComponents,
                /*projectionPlan*/ null,
                this.currentOperationResponse.CreateResponseMessage(),
                ODataPayloadKind.Entry);
        }
Example #20
0
        /// <summary>
        /// Get the source property Uri for the link URL
        /// </summary>
        /// <param name="binding">Link descriptor object of the binding</param>
        /// <param name="sourceEntityDescriptor">entity descriptor for source</param>
        /// <returns>source property Uri string</returns>
        private string GetSourcePropertyUri(LinkDescriptor binding, EntityDescriptor sourceEntityDescriptor)
        {
            Debug.Assert(binding != null, "binding != null");
            Debug.Assert(sourceEntityDescriptor != null, "sourceEntityDescriptor != null");

            if (string.IsNullOrEmpty(binding.SourceProperty))
            {
                return null;
            }

            string sourcePropertyUri = binding.SourceProperty;

            // Add type segment in the link URL for the derived entity type on which a navigation property is defined.
            // e.g. cxt.Attachto("<entitySetname>",<EntityToBeSource>)
            //      cxt.AddLink(<EntityToBeSource>, "<NavigationPropertyName>" <EntityToBeTarget>)
            // Get entity type name from model (here service model instead of client model should be used)
            string entityTypeFullName = this.RequestInfo.TypeResolver.ResolveServiceEntityTypeFullName(binding.Source.GetType());
            if (string.IsNullOrEmpty(entityTypeFullName))
            {
                return sourcePropertyUri;
            }

            // Get the type of entityset from service model.
            string sourceEntitySetTypeName = null;
            if (!string.IsNullOrEmpty(sourceEntityDescriptor.EntitySetName) && this.RequestInfo.TypeResolver.TryResolveEntitySetBaseTypeName(sourceEntityDescriptor.EntitySetName, out sourceEntitySetTypeName))
            {
                // Check whether the entity type and the entity set type are matched. if not matched, set the dervied entity type name as a key segment in the URL.
                if (!string.IsNullOrEmpty(sourceEntitySetTypeName) && !string.Equals(entityTypeFullName, sourceEntitySetTypeName, StringComparison.OrdinalIgnoreCase))
                {
                    sourcePropertyUri = entityTypeFullName + UriHelper.FORWARDSLASH + sourcePropertyUri;
                }
            }

            return sourcePropertyUri;
        }
Example #21
0
        public void PostTunnelingDeleteRequestShouldNotHaveContentTypeHeader()
        {
            bool previousPostTunnelingValue = ctx.UsePostTunneling;
            ctx.UsePostTunneling = true;
            HeaderCollection headersCollection = new HeaderCollection();
            var descriptor = new EntityDescriptor(this.clientEdmModel) { ServerTypeName = this.serverTypeName, Entity = new Customer() };
            var buildingRequestArgs = new BuildingRequestEventArgs("DELETE", new Uri("http://localhost/fakeService.svc/"), headersCollection, descriptor, HttpStack.Auto);

            ctx.Configurations.RequestPipeline.OnMessageCreating = (args) =>
            {
                buildingRequestArgs.Headers.Keys.Should().NotContain(XmlConstants.HttpContentType);
                return new HttpWebRequestMessage(args);
            };

            testSubject.CreateRequestMessage(buildingRequestArgs);

            // undoing change so this is applicable only for this test.
            ctx.UsePostTunneling = previousPostTunnelingValue;
            ctx.Configurations.RequestPipeline.OnMessageCreating = null;
        }
Example #22
0
 /// <summary>
 /// Get the materializer to process the response.
 /// </summary>
 /// <param name="entityDescriptor">entity descriptor whose response is getting materialized.</param>
 /// <param name="responseInfo">information about the response to be materialized.</param>
 /// <returns>an instance of MaterializeAtom, that can be used to materialize the response.</returns>
 protected abstract MaterializeAtom GetMaterializer(EntityDescriptor entityDescriptor, ResponseInfo responseInfo);
Example #23
0
        /// <summary>
        /// Creates an ODataEntry for the given EntityDescriptor and fills in its ODataLib metadata.
        /// </summary>
        /// <param name="entityDescriptor">The entity descriptor.</param>
        /// <param name="serverTypeName">Name of the server type.</param>
        /// <param name="entityType">The client-side entity type.</param>
        /// <param name="clientFormat">The current client format.</param>
        /// <returns>An odata entry with its metadata filled in.</returns>
        internal static ODataEntry CreateODataEntry(EntityDescriptor entityDescriptor, string serverTypeName, ClientTypeAnnotation entityType, DataServiceClientFormat clientFormat)
        {
            ODataEntry entry = new ODataEntry();

            // If the client type name is different from the server type name, then add SerializationTypeNameAnnotation
            // which tells ODataLib to write the type name in the annotation in the payload.
            if (entityType.ElementTypeName != serverTypeName)
            {
                entry.SetAnnotation(new SerializationTypeNameAnnotation { TypeName = serverTypeName });
            }

            // We always need to write the client type name, since this is the type name used by ODataLib
            // to resolve the entity type using EdmModel.FindSchemaElement.
            entry.TypeName = entityType.ElementTypeName;

            // Continue to send the entry's ID in update payloads in Atom for compatibility with V1-V3,
            // but for JSON-Light we do not want the extra information on the wire.
            if (clientFormat.UsingAtom && EntityStates.Modified == entityDescriptor.State)
            {
                // <id>http://host/service/entityset(key)</id>
                entry.Id = entityDescriptor.GetLatestIdentity();
            }

            if (entityDescriptor.IsMediaLinkEntry || entityType.IsMediaLinkEntry)
            {
                // Since we are already enabled EnableWcfDataServicesClientBehavior in the writer settings,
                // setting the MediaResource value will tell ODataLib to write MLE payload, irrespective of
                // what the metadata says.
                entry.MediaResource = new ODataStreamReferenceValue();
            }

            return entry;
        }
Example #24
0
        /// <summary>
        /// Create the response info instance to be passed to the materializer.
        /// </summary>
        /// <param name="entityDescriptor">entity descriptor whose response is getting handled.</param>
        /// <returns>instance of the response info class.</returns>
        protected ResponseInfo CreateResponseInfo(EntityDescriptor entityDescriptor)
        {
            MergeOption mergeOption = MergeOption.OverwriteChanges;

            // If we are processing a POST MR, we want to materialize the payload to get the metadata for the stream.
            // However we must not modify the MLE properties with the server initialized properties.  The next request
            // will be a Put MLE operation and we will set the server properties with values from the client entity.
            if (entityDescriptor.StreamState == EntityStates.Added)
            {
                mergeOption = MergeOption.PreserveChanges;
                Debug.Assert(entityDescriptor.State == EntityStates.Modified, "The MLE state must be Modified.");
            }

            return this.RequestInfo.GetDeserializationInfo(mergeOption);
        }
Example #25
0
        /// <summary>
        /// Writes a navigation link.
        /// </summary>
        /// <param name="entityDescriptor">The entity</param>
        /// <param name="relatedLinks">The links related to the entity</param>
        /// <param name="odataWriter">The ODataWriter used to write the navigation link.</param>
        internal void WriteNavigationLink(EntityDescriptor entityDescriptor, IEnumerable<LinkDescriptor> relatedLinks, ODataWriterWrapper odataWriter)
        {
            // TODO: create instance of odatawriter.
            // TODO: send clientType once, so that we dont need entity descriptor
            Debug.Assert(EntityStates.Added == entityDescriptor.State, "entity not added state");

            Dictionary<string, List<LinkDescriptor>> groupRelatedLinks = new Dictionary<string, List<LinkDescriptor>>(EqualityComparer<string>.Default);
            foreach (LinkDescriptor end in relatedLinks)
            {
                List<LinkDescriptor> linkDescriptorsList = null;
                if (!groupRelatedLinks.TryGetValue(end.SourceProperty, out linkDescriptorsList))
                {
                    linkDescriptorsList = new List<LinkDescriptor>();
                    groupRelatedLinks.Add(end.SourceProperty, linkDescriptorsList);
                }

                linkDescriptorsList.Add(end);
            }

            ClientTypeAnnotation clientType = null;
            foreach (var grlinks in groupRelatedLinks)
            {
                if (null == clientType)
                {
                    ClientEdmModel model = this.requestInfo.Model;
                    clientType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));
                }

                bool isCollection = clientType.GetProperty(grlinks.Key, false).IsEntityCollection;
                bool started = false;

                foreach (LinkDescriptor end in grlinks.Value)
                {
                    Debug.Assert(!end.ContentGeneratedForSave, "already saved link");
                    end.ContentGeneratedForSave = true;
                    Debug.Assert(null != end.Target, "null is DELETE");

                    ODataNavigationLink navigationLink = new ODataNavigationLink();
                    navigationLink.Url = this.requestInfo.EntityTracker.GetEntityDescriptor(end.Target).GetLatestEditLink();
                    Debug.Assert(Uri.IsWellFormedUriString(UriUtil.UriToString(navigationLink.Url), UriKind.Absolute), "Uri.IsWellFormedUriString(targetEditLink, UriKind.Absolute)");

                    navigationLink.IsCollection = isCollection;
                    navigationLink.Name = grlinks.Key;

                    if (!started)
                    {
                        odataWriter.WriteNavigationLinksStart(navigationLink);
                        started = true;
                    }

                    odataWriter.WriteNavigationLinkStart(navigationLink, end.Source, end.Target);
                    odataWriter.WriteEntityReferenceLink(new ODataEntityReferenceLink() { Url = navigationLink.Url }, end.Source, end.Target);
                    odataWriter.WriteNavigationLinkEnd(navigationLink, end.Source, end.Target);
                }

                odataWriter.WriteNavigationLinksEnd();
            }
        }
Example #26
0
        /// <summary>
        /// enumerate the related Modified/Unchanged links for an added item
        /// </summary>
        /// <param name="entityDescriptor">entity</param>
        /// <returns>related links</returns>
        /// <remarks>
        /// During a non-batch SaveChanges, an Added entity can become an Unchanged entity
        /// and should be included in the set of related links for the second Added entity.
        /// </remarks>
        protected IEnumerable<LinkDescriptor> RelatedLinks(EntityDescriptor entityDescriptor)
        {
            foreach (LinkDescriptor end in this.RequestInfo.EntityTracker.Links)
            {
                if (end.Source == entityDescriptor.Entity)
                {
                    if (null != end.Target)
                    {   // null TargetResource is equivalent to Deleted
                        EntityDescriptor target = this.RequestInfo.EntityTracker.GetEntityDescriptor(end.Target);

                        // assumption: the source entity started in the Added state
                        // note: SaveChanges operates with two passes
                        //      a) first send the request and then attach identity and append the result into a batch response  (Example: BeginSaveChanges)
                        //      b) process the batch response (shared code with SaveChanges(BatchWithSingleChangeset))  (Example: EndSaveChanges)
                        // note: SaveResultWasProcessed is set when to the pre-save state when the save result is sucessfully processed

                        // scenario #1 when target entity started in modified or unchanged state
                        // 1) the link target entity was modified and now implicitly assumed to be unchanged (this is true in second pass)
                        // 2) or link target entity has not been saved is in the modified or unchanged state (this is true in first pass)

                        // scenario #2 when target entity started in added state
                        // 1) target entity has an identity (true in first pass for non-batch)
                        // 2) target entity is processed before source to qualify (1) better during the second pass
                        // 3) the link target has not been saved and is in the added state
                        // 4) or the link target has been saved and was in the added state
                        if (Util.IncludeLinkState(target.SaveResultWasProcessed) || ((0 == target.SaveResultWasProcessed) && Util.IncludeLinkState(target.State)) ||
                            ((null != target.Identity) && (target.ChangeOrder < entityDescriptor.ChangeOrder) &&
                             ((0 == target.SaveResultWasProcessed && EntityStates.Added == target.State) ||
                              (EntityStates.Added == target.SaveResultWasProcessed))))
                        {
                            Debug.Assert(entityDescriptor.ChangeOrder < end.ChangeOrder, "saving is out of order");
                            yield return end;
                        }
                    }
                }
            }
        }
Example #27
0
        internal bool DetachResource(EntityDescriptor resource)
        {
            this.EnsureLinkBindings();

            // Since we are changing the list on the fly, we need to convert it into a list first
            // so that enumeration won't get effected.
            foreach (LinkDescriptor end in this.bindings.Values.Where(resource.IsRelatedEntity).ToList())
            {
                this.DetachExistingLink(
                        end,
                        end.Target == resource.Entity && resource.State == EntityStates.Added);
            }

            resource.ChangeOrder = UInt32.MaxValue;
            resource.State = EntityStates.Detached;
            bool flag = this.entityDescriptors.Remove(resource.Entity);
            Debug.Assert(flag, "should have removed existing entity");
            this.DetachResourceIdentity(resource);

            return true;
        }
Example #28
0
        /// <summary>
        /// Create ODataRequestMessage for the given entity.
        /// </summary>
        /// <param name="entityDescriptor">resource</param>
        /// <returns>An instance of ODataRequestMessage for the given entity.</returns>
        protected ODataRequestMessageWrapper CreateRequest(EntityDescriptor entityDescriptor)
        {
            Debug.Assert(null != entityDescriptor, "null entityDescriptor");
            Debug.Assert(entityDescriptor.State == EntityStates.Added || entityDescriptor.State == EntityStates.Deleted || entityDescriptor.State == EntityStates.Modified, "the entity must be in one of the 3 possible states");

            EntityStates state = entityDescriptor.State;
            Uri requestUri = entityDescriptor.GetResourceUri(this.RequestInfo.BaseUriResolver, false /*queryLink*/);

            Debug.Assert(null != requestUri, "request uri is null");
            Debug.Assert(requestUri.IsAbsoluteUri, "request uri is not absolute uri");

            ClientEdmModel model = this.RequestInfo.Model;
            ClientTypeAnnotation clientType = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));
            Version requestVersion = DetermineRequestVersion(clientType);
            string httpMethod = this.GetHttpMethod(state, ref requestVersion);

            HeaderCollection headers = new HeaderCollection();

            // Set the content type
            if (EntityStates.Deleted != entityDescriptor.State)
            {
                this.RequestInfo.Context.Format.SetRequestContentTypeForEntry(headers);
            }

            // Set IfMatch (etag) header for update and delete requests
            if ((EntityStates.Deleted == state) || (EntityStates.Modified == state))
            {
                string etag = entityDescriptor.GetLatestETag();
                if (etag != null)
                {
                    headers.SetHeader(XmlConstants.HttpRequestIfMatch, etag);
                }
            }

            // Set the prefer header if required
            ApplyPreferences(headers, httpMethod, this.RequestInfo.AddAndUpdateResponsePreference, ref requestVersion);

            // Set the request DSV and request MDSV headers
            headers.SetRequestVersion(requestVersion, this.RequestInfo.MaxProtocolVersionAsVersion);

            this.RequestInfo.Format.SetRequestAcceptHeader(headers);
            return this.CreateRequestMessage(httpMethod, requestUri, headers, this.RequestInfo.HttpStack, entityDescriptor, this.IsBatchRequest ? entityDescriptor.ChangeOrder.ToString(CultureInfo.InvariantCulture) : null);
        }
Example #29
0
        /// <summary>response materialization has an identity to attach to the inserted object</summary>
        /// <param name="entityDescriptorFromMaterializer">entity descriptor containing all the information about the entity from the response.</param>
        /// <param name="metadataMergeOption">mergeOption based on which EntityDescriptor will be merged.</param>
        internal override void AttachIdentity(EntityDescriptor entityDescriptorFromMaterializer, MergeOption metadataMergeOption)
        {   // insert->unchanged
            Debug.Assert(entityDescriptorFromMaterializer != null, "entityDescriptorFromMaterializer != null");

            this.EnsureIdentityToResource();

            // resource.State == EntityState.Added or Unchanged for second pass of media link
            EntityDescriptor trackedEntityDescriptor = this.entityDescriptors[entityDescriptorFromMaterializer.Entity];

            // make sure we got the right one - server could override identity and we may be tracking another one already.
            this.ValidateDuplicateIdentity(entityDescriptorFromMaterializer.Identity, trackedEntityDescriptor);

            this.DetachResourceIdentity(trackedEntityDescriptor);

            // While processing the response, we need to find out if the given resource was inserted deep
            // If it was, then we need to change the link state from added to unchanged
            if (trackedEntityDescriptor.IsDeepInsert)
            {
                LinkDescriptor end = this.bindings[trackedEntityDescriptor.GetRelatedEnd()];
                end.State = EntityStates.Unchanged;
            }

            trackedEntityDescriptor.Identity = entityDescriptorFromMaterializer.Identity; // always attach the identity
            AtomMaterializerLog.MergeEntityDescriptorInfo(trackedEntityDescriptor, entityDescriptorFromMaterializer, true /*mergeInfo*/, metadataMergeOption);
            trackedEntityDescriptor.State = EntityStates.Unchanged;
            trackedEntityDescriptor.PropertiesToSerialize.Clear();

            // scenario: sucessfully (1) delete an existing entity and (2) add a new entity where the new entity has the same identity as deleted entity
            // where the SaveChanges pass1 will now associate existing identity with new entity
            // but pass2 for the deleted entity will not blindly remove the identity that is now associated with the new identity
            this.identityToDescriptor[entityDescriptorFromMaterializer.Identity] = trackedEntityDescriptor;
        }
Example #30
0
        /// <summary>
        /// Get the server type name - either from the entity descriptor or using the type resolver.
        /// </summary>
        /// <param name="descriptor">The entity descriptor.</param>
        /// <returns>The server type name for the entity.</returns>
        internal string GetServerTypeName(EntityDescriptor descriptor)
        {
            Debug.Assert(descriptor != null && descriptor.Entity != null, "Null descriptor or no entity in descriptor");
            string serverTypeName = null;

            if (this.HasResolveName)
            {
                Type entityType = descriptor.Entity.GetType();
                if (this.IsUserSuppliedResolver)
                {
                    // User-supplied resolver, must call first
                    serverTypeName = this.ResolveNameFromType(entityType) ?? descriptor.GetLatestServerTypeName();
                }
                else
                {
                    // V2+ codegen resolver, called last
                    serverTypeName = descriptor.GetLatestServerTypeName() ?? this.ResolveNameFromType(entityType);
                }
            }
            else
            {
                serverTypeName = descriptor.GetLatestServerTypeName();
            }

            return serverTypeName;
        }
Example #31
0
 /// <summary>
 /// Ensure an identity is unique and does not point to another resource
 /// </summary>
 /// <param name="identity">The identity</param>
 /// <param name="descriptor">The entity descriptor</param>
 private void ValidateDuplicateIdentity(Uri identity, EntityDescriptor descriptor)
 {
     EntityDescriptor trackedIdentity;
     if (this.identityToDescriptor.TryGetValue(identity, out trackedIdentity) && descriptor != trackedIdentity && trackedIdentity.State != EntityStates.Deleted && trackedIdentity.State != EntityStates.Detached)
     {
         // we checked the state because we do not remove the deleted/detached entity descriptor from the dictionary until we have finished processing all changes
         // So for instance if you delete one entity and add back one with the same ID, they will be a temporary conflict in the dictionary.
         throw Error.InvalidOperation(Strings.Context_DifferentEntityAlreadyContained);
     }
 }
Example #32
0
        /// <summary>
        /// Generate the batch request for all changes to save.
        /// </summary>
        /// <returns>Returns the instance of ODataRequestMessage containing all the headers and payload for the batch request.</returns>
        private ODataRequestMessageWrapper GenerateBatchRequest()
        {
            if (this.ChangedEntries.Count == 0 && this.Queries == null)
            {
                this.SetCompleted();
                return(null);
            }

            ODataRequestMessageWrapper batchRequestMessage = this.CreateBatchRequest();

            // we need to fire request after the headers have been written, but before we write the payload
            batchRequestMessage.FireSendingRequest2(null);

            using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(batchRequestMessage, this.RequestInfo, false /*isParameterPayload*/))
            {
                this.batchWriter = messageWriter.CreateODataBatchWriter();
                this.batchWriter.WriteStartBatch();

                if (this.Queries != null)
                {
                    foreach (DataServiceRequest query in this.Queries)
                    {
                        QueryComponents queryComponents = query.QueryComponents(this.RequestInfo.Model);
                        Uri             requestUri      = this.RequestInfo.BaseUriResolver.GetOrCreateAbsoluteUri(queryComponents.Uri);

                        Debug.Assert(requestUri != null, "request uri is null");
                        Debug.Assert(requestUri.IsAbsoluteUri, "request uri is not absolute uri");

                        HeaderCollection headers = new HeaderCollection();

                        headers.SetRequestVersion(queryComponents.Version, this.RequestInfo.MaxProtocolVersionAsVersion);

                        this.RequestInfo.Format.SetRequestAcceptHeaderForQuery(headers, queryComponents);

                        ODataRequestMessageWrapper batchOperationRequestMessage = this.CreateRequestMessage(XmlConstants.HttpMethodGet, requestUri, headers, this.RequestInfo.HttpStack, null /*descriptor*/, null /*contentId*/);

                        batchOperationRequestMessage.FireSendingEventHandlers(null /*descriptor*/);
                    }
                }
                else if (0 < this.ChangedEntries.Count)
                {
                    if (Util.IsBatchWithSingleChangeset(this.Options))
                    {
                        this.batchWriter.WriteStartChangeset();
                    }

                    var model = this.RequestInfo.Model;

                    for (int i = 0; i < this.ChangedEntries.Count; ++i)
                    {
                        if (Util.IsBatchWithIndependentOperations(this.Options))
                        {
                            this.batchWriter.WriteStartChangeset();
                        }

                        Descriptor descriptor = this.ChangedEntries[i];
                        if (descriptor.ContentGeneratedForSave)
                        {
                            continue;
                        }

                        EntityDescriptor entityDescriptor = descriptor as EntityDescriptor;
                        if (descriptor.DescriptorKind == DescriptorKind.Entity)
                        {
                            if (entityDescriptor.State == EntityStates.Added)
                            {
                                // We don't support adding MLE/MR in batch mode
                                ClientTypeAnnotation type = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));
                                if (type.IsMediaLinkEntry || entityDescriptor.IsMediaLinkEntry)
                                {
                                    throw Error.NotSupported(Strings.Context_BatchNotSupportedForMediaLink);
                                }
                            }
                            else if (entityDescriptor.State == EntityStates.Unchanged || entityDescriptor.State == EntityStates.Modified)
                            {
                                // We don't support PUT for the MR in batch mode
                                // It's OK to PUT the MLE alone inside a batch mode though
                                if (entityDescriptor.SaveStream != null)
                                {
                                    throw Error.NotSupported(Strings.Context_BatchNotSupportedForMediaLink);
                                }
                            }
                        }
                        else if (descriptor.DescriptorKind == DescriptorKind.NamedStream)
                        {
                            // Similar to MR, we do not support adding named streams in batch mode.
                            throw Error.NotSupported(Strings.Context_BatchNotSupportedForNamedStreams);
                        }

                        ODataRequestMessageWrapper operationRequestMessage;
                        if (descriptor.DescriptorKind == DescriptorKind.Entity)
                        {
                            operationRequestMessage = this.CreateRequest(entityDescriptor);
                        }
                        else
                        {
                            operationRequestMessage = this.CreateRequest((LinkDescriptor)descriptor);
                        }

                        // we need to fire request after the headers have been written, but before we write the payload
                        operationRequestMessage.FireSendingRequest2(descriptor);

                        this.CreateChangeData(i, operationRequestMessage);

                        if (Util.IsBatchWithIndependentOperations(this.Options))
                        {
                            this.batchWriter.WriteEndChangeset();
                        }
                    }

                    if (Util.IsBatchWithSingleChangeset(this.Options))
                    {
                        this.batchWriter.WriteEndChangeset();
                    }
                }

                this.batchWriter.WriteEndBatch();
                this.batchWriter.Flush();
            }

            Debug.Assert(this.ChangedEntries.All(o => o.ContentGeneratedForSave), "didn't generated content for all entities/links");
            return(batchRequestMessage);
        }