/// <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; }
/// <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); }
/// <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: successfully (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; }
/// <summary>use location from header to generate initial edit and identity</summary> /// <param name="entity">entity in added state</param> /// <param name="identity">identity as specified in the response header - location header or OData-EntityId header.</param> /// <param name="editLink">editlink as specified in the response header - location header.</param> internal void AttachLocation(object entity, Uri identity, Uri editLink) { Debug.Assert(entity != null, "null != entity"); Debug.Assert(editLink != null, "editLink != null"); this.EnsureIdentityToResource(); // resource.State == EntityState.Added or Unchanged for second pass of media link EntityDescriptor resource = this.entityDescriptors[entity]; // make sure we got the right one - server could override identity and we may be tracking another one already. this.ValidateDuplicateIdentity(identity, resource); this.DetachResourceIdentity(resource); // 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 (resource.IsDeepInsert) { LinkDescriptor end = this.bindings[resource.GetRelatedEnd()]; end.State = EntityStates.Unchanged; } resource.Identity = identity; // always attach the identity resource.EditLink = editLink; // scenario: successfully batch (1) add a new entity and (2) delete an existing 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[identity] = resource; }
/// <summary>Detach existing link</summary> /// <param name="existingLink">link to detach</param> /// <param name="targetDelete">true if target is being deleted, false otherwise</param> internal override void DetachExistingLink(LinkDescriptor existingLink, bool targetDelete) { // The target can be null in which case we don't need this check if (existingLink.Target != null) { // Identify the target resource for the link EntityDescriptor targetResource = this.GetEntityDescriptor(existingLink.Target); // Check if there is a dependency relationship b/w the source and target objects i.e. target can not exist without source link // Deep insert requires this check to be made but skip the check if the target object is being deleted if (targetResource.IsDeepInsert && !targetDelete) { EntityDescriptor parentOfTarget = targetResource.ParentForInsert; if (Object.ReferenceEquals(targetResource.ParentEntity, existingLink.Source) && (parentOfTarget.State != EntityStates.Deleted || parentOfTarget.State != EntityStates.Detached)) { throw new InvalidOperationException(Strings.Context_ChildResourceExists); } } } if (this.TryRemoveLinkDescriptor(existingLink)) { // this link may have been previously detached by a detaching entity existingLink.State = EntityStates.Detached; } }
internal void AddedLink(MaterializerEntry source, string propertyName, object target) { if (this.Tracking && (ShouldTrackWithContext(source) && ShouldTrackWithContext(target, this.responseInfo.MaxProtocolVersion))) { LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Added); this.links.Add(item); } }
internal void AddLink(LinkDescriptor linkDescriptor) { try { this.bindings.Add(linkDescriptor, linkDescriptor); } catch (ArgumentException) { throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Context_RelationAlreadyContained); } }
/// <summary> /// Gets string representation of the specified link descriptor. /// </summary> /// <param name="linkDescriptor">The link descriptor.</param> /// <returns>The string representation of the specified link descriptor.</returns> public static string ToTraceString(this LinkDescriptor linkDescriptor) { ExceptionUtilities.CheckArgumentNotNull(linkDescriptor, "linkDescriptor"); return(string.Format( CultureInfo.InvariantCulture, "{{ State = {0}, Source = {{ {1} }}, SourceProperty = '{2}', Target = {{ {3} }} }}", linkDescriptor.State, linkDescriptor.Source, linkDescriptor.SourceProperty, linkDescriptor.Target == null ? "null" : linkDescriptor.Target)); }
/// <summary> /// Invoke this method to notify the log that a link was removed /// from a collection. /// </summary> /// <param name="source"> /// Instance with the collection from which <paramref name="target"/> /// was removed. /// </param> /// <param name="propertyName">Property name for collection.</param> /// <param name="target">Object which was removed.</param> internal void RemovedLink(MaterializerEntry source, string propertyName, object target) { Debug.Assert(source.Entry != null || source.ForLoadProperty, "source != null || source.ForLoadProperty"); Debug.Assert(propertyName != null, "propertyName != null"); if (IsEntity(source) && IsEntity(target, this.clientEdmModel)) { Debug.Assert(this.Tracking, "this.Tracking -- otherwise there's an 'if' missing (it happens to be that the assert holds for all current callers"); LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Detached); this.links.Add(item); } }
private string ToTraceString(LinkDescriptor descriptor) { ExceptionUtilities.Assert(descriptor != null, "descriptor cannot be null."); return(string.Format( CultureInfo.InvariantCulture, "{{ State = {0}, Source = {{ Identity = {1} }}, SourceProperty = '{2}', Target = {{ {3} }} }}", descriptor.State, this.context.GetEntityDescriptor(descriptor.Source).Identity, descriptor.SourceProperty, descriptor.Target == null ? "null" : "Identity = " + this.context.GetEntityDescriptor(descriptor.Target).Identity)); }
private void ApplicationStartup(object sender, StartupEventArgs e) { var destinationPath = Path.GetDirectoryName(e.Args[0]); var linkDescriptor = new LinkDescriptor { DestinationPath = destinationPath }; var initialStep = new LinkTypeWizardStep(linkDescriptor); var wizardWindow = new WizardWindow("Создание ссылки", initialStep); MainWindow = wizardWindow; MainWindow.Show(); }
/// <summary> /// Add the given link to the link descriptor collection /// </summary> /// <param name="linkDescriptor">link descriptor to add</param> /// <exception cref="InvalidOperationException">throws argument exception if the link already exists</exception> internal void AddLink(LinkDescriptor linkDescriptor) { Debug.Assert(linkDescriptor != null, "linkDescriptor != null"); try { this.EnsureLinkBindings(); this.bindings.Add(linkDescriptor, linkDescriptor); } catch (ArgumentException) { throw Error.InvalidOperation(Strings.Context_RelationAlreadyContained); } }
public void SerializeEnity_NullableEnumProperty() { MyEntity1 myEntity1 = new MyEntity1() { ID = 2, MyColorValue = null, MyFlagsColorValue = MyFlagsColor.Blue, ComplexValue1Value = new ComplexValue1() { MyColorValue = MyColor.Green, MyFlagsColorValue = MyFlagsColor.Red }, MyFlagsColorCollection1 = new List <MyFlagsColor>() { MyFlagsColor.Blue, MyFlagsColor.Red, MyFlagsColor.Red }, MyColorCollection = new List <MyColor?> { MyColor.Green, null } }; DataServiceContext dataServiceContext = new DataServiceContext(new Uri("http://www.odata.org/service.svc")); dataServiceContext.EnableAtom = true; dataServiceContext.Format.UseAtom(); dataServiceContext.AttachTo("MyEntitySet1", myEntity1); 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 = myEntity1; var requestMessageArgs = new BuildingRequestEventArgs("POST", new Uri("http://www.foo.com/Northwind"), headers, entityDescriptor, HttpStack.Auto); var linkDescriptors = new LinkDescriptor[] { }; var odataRequestMessageWrapper = ODataRequestMessageWrapper.CreateRequestMessageWrapper(requestMessageArgs, requestInfo); serializer.WriteEntry(entityDescriptor, linkDescriptors, odataRequestMessageWrapper); // read result: MemoryStream stream = (MemoryStream)(odataRequestMessageWrapper.CachedRequestStream.Stream); stream.Position = 0; string payload = (new StreamReader(stream)).ReadToEnd(); payload = Regex.Replace(payload, "<updated>[^<]*</updated>", ""); payload.Should().Be( "{\"ComplexValue1Value\":{\"MyColorValue\":\"Green\",\"MyFlagsColorValue\":\"Red\",\"StringValue\":null},\"ID\":2,\"MyColorCollection\":[\"Green\",null],\"MyColorValue\":null,\"MyFlagsColorCollection1\":[\"Blue\",\"Red\",\"Red\"],\"MyFlagsColorValue\":\"Blue\"}"); }
/// <summary> /// attach the link with the given source, sourceProperty and target. /// </summary> /// <param name="source">source entity of the link.</param> /// <param name="sourceProperty">name of the property on the source entity.</param> /// <param name="target">target entity of the link.</param> /// <param name="linkMerge">merge option to be used to merge the link if there is an existing link.</param> internal override void AttachLink(object source, string sourceProperty, object target, MergeOption linkMerge) { LinkDescriptor relation = new LinkDescriptor(source, sourceProperty, target, this.model); LinkDescriptor existing = this.TryGetLinkDescriptor(source, sourceProperty, target); if (existing != null) { switch (linkMerge) { case MergeOption.AppendOnly: break; case MergeOption.OverwriteChanges: relation = existing; break; case MergeOption.PreserveChanges: if ((existing.State == EntityStates.Added) || (existing.State == EntityStates.Unchanged) || (existing.State == EntityStates.Modified && existing.Target != null)) { relation = existing; } break; case MergeOption.NoTracking: // public API point should throw if link exists throw Error.InvalidOperation(Strings.Context_RelationAlreadyContained); } } else { if (this.model.GetClientTypeAnnotation(this.model.GetOrCreateEdmType(source.GetType())).GetProperty(sourceProperty, UndeclaredPropertyBehavior.ThrowException).IsEntityCollection || ((existing = this.DetachReferenceLink(source, sourceProperty, target, linkMerge)) == null)) { this.AddLink(relation); this.IncrementChange(relation); } else if (!((MergeOption.AppendOnly == linkMerge) || (MergeOption.PreserveChanges == linkMerge && EntityStates.Modified == existing.State))) { // AppendOnly doesn't change state or target // OverWriteChanges changes target and state // PreserveChanges changes target if unchanged, leaves modified target and state alone relation = existing; } } relation.State = EntityStates.Unchanged; }
private void VerifyDescriptor(DescriptorData expected, Descriptor actual, int responseOrder) { EntityDescriptorData entityDescriptorData = expected as EntityDescriptorData; LinkDescriptorData linkDescriptorData = expected as LinkDescriptorData; StreamDescriptorData streamDescriptorData = expected as StreamDescriptorData; if (entityDescriptorData != null) { EntityDescriptor entityDescriptor = actual as EntityDescriptor; this.Assert.IsNotNull(entityDescriptor, GetVerificationFailureMessage(responseOrder, "Unexpected descriptor type:\r\nExpected: {0}\r\nActual: {1}\r\nExpected descriptor data: {2}.", typeof(EntityDescriptor).Name, actual.GetType().Name, entityDescriptorData)); this.Assert.AreSame( entityDescriptorData.Entity, entityDescriptor.Entity, GetVerificationFailureMessage(responseOrder, "Entity verification failed for the entity descriptor data: {0}.", expected)); } else if (linkDescriptorData != null) { LinkDescriptor linkDescriptor = actual as LinkDescriptor; this.Assert.IsNotNull(linkDescriptor, GetVerificationFailureMessage(responseOrder, "Unexpected descriptor type:\r\nExpected: {0}\r\nActual: {1}\r\nExpected descriptor data: {2}.", typeof(LinkDescriptor).Name, actual.GetType().Name, linkDescriptorData)); bool notMatch = linkDescriptorData.SourceDescriptor.Entity != linkDescriptor.Source || (linkDescriptorData.TargetDescriptor == null && linkDescriptor.Target != null) || (linkDescriptorData.TargetDescriptor != null && linkDescriptorData.TargetDescriptor.Entity != linkDescriptor.Target) || linkDescriptorData.SourcePropertyName != linkDescriptor.SourceProperty; this.Assert.IsFalse(notMatch, GetVerificationFailureMessage(responseOrder, "Link verification failed.\r\nExpected: {0}\r\nActual: {1}", linkDescriptorData, linkDescriptor.ToTraceString())); } else { #if WINDOWS_PHONE throw new TaupoNotSupportedException("StreamDescriptors are not supported on Windows Phone"); #else ExceptionUtilities.CheckObjectNotNull(streamDescriptorData, "Expected was not an entity, link, or stream descriptor: {0}", expected); StreamDescriptor streamDescriptor = actual as StreamDescriptor; this.Assert.IsNotNull(streamDescriptor, GetVerificationFailureMessage(responseOrder, "Unexpected descriptor type:\r\nExpected: {0}\r\nActual: {1}\r\nExpected descriptor data: {2}.", typeof(StreamDescriptor).Name, actual.GetType().Name, streamDescriptorData)); this.Assert.AreEqual(streamDescriptorData.State.ToProductEnum(), streamDescriptor.State, GetVerificationFailureMessage(responseOrder, "Stream descriptor state verification failed.")); this.Assert.AreEqual(streamDescriptorData.Name, streamDescriptor.StreamLink.Name, GetVerificationFailureMessage(responseOrder, "Stream descriptor name verification failed.")); this.Assert.AreEqual(streamDescriptorData.ETag, streamDescriptor.StreamLink.ETag, GetVerificationFailureMessage(responseOrder, "Stream descriptor etag verification failed.")); this.Assert.AreEqual(streamDescriptorData.ContentType, streamDescriptor.StreamLink.ContentType, GetVerificationFailureMessage(responseOrder, "Stream descriptor content type verification failed.")); this.Assert.AreEqual(streamDescriptorData.EditLink, streamDescriptor.StreamLink.EditLink, GetVerificationFailureMessage(responseOrder, "Stream descriptor edit link verification failed.")); this.Assert.AreEqual(streamDescriptorData.SelfLink, streamDescriptor.StreamLink.SelfLink, GetVerificationFailureMessage(responseOrder, "Stream descriptor self link verification failed.")); #endif } }
public void SerializeEnity_TwoNavigationLinksInJsonFormat() { var person = new Person { ID = 100, Name = "Bing", }; var car1 = new Car { ID = 1001 }; var car2 = new Car { ID = 1002 }; DataServiceContext dataServiceContext = new DataServiceContext(new Uri("http://www.odata.org/service.svc")); dataServiceContext.AttachTo("Persons", person); dataServiceContext.AttachTo("Cars", car1); dataServiceContext.AttachTo("Cars", car2); dataServiceContext.AddLink(person, "Cars", car1); dataServiceContext.AddLink(person, "Cars", car2); 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; entityDescriptor.EditLink = new Uri("http://www.foo.com/custom"); 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, "Cars", car2, clientModel) }; var odataRequestMessageWrapper = ODataRequestMessageWrapper.CreateRequestMessageWrapper(requestMessageArgs, requestInfo); serializer.WriteEntry(entityDescriptor, linkDescriptors, odataRequestMessageWrapper); // read result: MemoryStream stream = (MemoryStream)(odataRequestMessageWrapper.CachedRequestStream.Stream); stream.Position = 0; string payload = (new StreamReader(stream)).ReadToEnd(); payload.Should().Be( "{\"ID\":100,\"Name\":\"Bing\",\"[email protected]\":[\"http://www.odata.org/service.svc/Cars(1001)\",\"http://www.odata.org/service.svc/Cars(1002)\"]}"); }
/// <summary> /// Invoke this method to notify the log that a new link was /// added to a collection. /// </summary> /// <param name="source"> /// Instance with the collection to which <paramref name="target"/> /// was added. /// </param> /// <param name="propertyName">Property name for collection.</param> /// <param name="target">Object which was added.</param> internal void AddedLink(MaterializerEntry source, string propertyName, object target) { Debug.Assert(source.Entry != null || source.ForLoadProperty, "source != null || source.ForLoadProperty"); Debug.Assert(propertyName != null, "propertyName != null"); if (!this.Tracking) { return; } if (IsEntity(source) && IsEntity(target, this.clientEdmModel)) { LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Added); this.links.Add(item); } }
/// <summary> /// find and detach link for reference property /// </summary> /// <param name="source">source entity</param> /// <param name="sourceProperty">source entity property name for target entity</param> /// <param name="target">target entity</param> /// <param name="linkMerge">link merge option</param> /// <returns>true if found and not removed</returns> internal LinkDescriptor DetachReferenceLink(object source, string sourceProperty, object target, MergeOption linkMerge) { Debug.Assert(sourceProperty.IndexOf('/') == -1, "sourceProperty.IndexOf('/') == -1"); LinkDescriptor existing = this.GetLinks(source, sourceProperty).FirstOrDefault(); if (existing != null) { if ((target == existing.Target) || (MergeOption.AppendOnly == linkMerge) || (MergeOption.PreserveChanges == linkMerge && EntityStates.Modified == existing.State)) { return(existing); } // Since we don't support deep insert on reference property, no need to check for deep insert. this.DetachExistingLink(existing, false); Debug.Assert(!this.Links.Any(o => (o.Source == source) && (o.SourceProperty == sourceProperty)), "only expecting one"); } return(null); }
private string SerializeEntity <T>(string entitySetName, T entityObject) { this.context.AddObject(entitySetName, entityObject); var requestInfo = new RequestInfo(this.context); var serializer = new Serializer(requestInfo); var headers = new HeaderCollection(); var entityDescriptor = new EntityDescriptor(this.clientEdmModel); entityDescriptor.State = EntityStates.Added; entityDescriptor.Entity = entityObject; var requestMessageArgs = new BuildingRequestEventArgs("POST", new Uri("http://tempuri.org"), headers, entityDescriptor, HttpStack.Auto); var odataRequestMessageWrapper = ODataRequestMessageWrapper.CreateRequestMessageWrapper(requestMessageArgs, requestInfo); var linkDescriptors = new LinkDescriptor[0]; serializer.WriteEntry(entityDescriptor, linkDescriptors, odataRequestMessageWrapper); var stream = (MemoryStream)odataRequestMessageWrapper.CachedRequestStream.Stream; var streamReader = new StreamReader(stream); var body = streamReader.ReadToEnd(); return(body); }
/// <summary> /// Writes an entity reference link. /// </summary> /// <param name="binding">The link descriptor.</param> /// <param name="requestMessage">The request message used for writing the payload.</param> internal void WriteEntityReferenceLink(LinkDescriptor binding, ODataRequestMessageWrapper requestMessage) #endif { using (ODataMessageWriter messageWriter = Serializer.CreateMessageWriter(requestMessage, this.requestInfo, false /*isParameterPayload*/)) { EntityDescriptor targetResource = this.requestInfo.EntityTracker.GetEntityDescriptor(binding.Target); Uri targetReferenceLink = targetResource.GetLatestIdentity(); if (targetReferenceLink == null) { #if DEBUG Debug.Assert(isBatch, "we should be cross-referencing entities only in batch scenarios"); #endif targetReferenceLink = UriUtil.CreateUri("$" + targetResource.ChangeOrder.ToString(CultureInfo.InvariantCulture), UriKind.Relative); } ODataEntityReferenceLink referenceLink = new ODataEntityReferenceLink(); referenceLink.Url = targetReferenceLink; messageWriter.WriteEntityReferenceLink(referenceLink); } }
internal void AttachLink(object source, string sourceProperty, object target, MergeOption linkMerge) { LinkDescriptor linkDescriptor = new LinkDescriptor(source, sourceProperty, target, this.maxProtocolVersion); LinkDescriptor descriptor2 = this.TryGetLinkDescriptor(source, sourceProperty, target); if (descriptor2 != null) { switch (linkMerge) { case MergeOption.OverwriteChanges: linkDescriptor = descriptor2; break; case MergeOption.PreserveChanges: if (((EntityStates.Added == descriptor2.State) || (EntityStates.Unchanged == descriptor2.State)) || ((EntityStates.Modified == descriptor2.State) && (descriptor2.Target != null))) { linkDescriptor = descriptor2; } break; case MergeOption.NoTracking: throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Context_RelationAlreadyContained); } } else { ClientEdmModel model = ClientEdmModel.GetModel(this.maxProtocolVersion); if (model.GetClientTypeAnnotation(model.GetOrCreateEdmType(source.GetType())).GetProperty(sourceProperty, false).IsEntityCollection || ((descriptor2 = this.DetachReferenceLink(source, sourceProperty, target, linkMerge)) == null)) { this.AddLink(linkDescriptor); this.IncrementChange(linkDescriptor); } else if ((linkMerge != MergeOption.AppendOnly) && ((MergeOption.PreserveChanges != linkMerge) || (EntityStates.Modified != descriptor2.State))) { linkDescriptor = descriptor2; } } linkDescriptor.State = EntityStates.Unchanged; }
// Introduce 90-degree angle vertices to the lines if they're not fully straight void FixLines() { // Straight line tolerance expressed as maximum vertical offset between two vertices of the line const float verticalTolerance = 10.0f; const float connectorPadding = 20.0f; LineRenderer[] lines = lineCanvas.GetComponentsInChildren <LineRenderer>(); foreach (LineRenderer line in lines) { LinkDescriptor linkDesc = line.GetComponent <LinkDescriptor>(); Vector2 finalPoint = line.GetPosition(1); if (Mathf.Abs(line.GetPosition(0).y - finalPoint.y) > verticalTolerance) { RectTransform prevTransform = linkDesc.prev.GetComponent <RectTransform>(), nextTransform = linkDesc.next.GetComponent <RectTransform>(); Rect prevRect = prevTransform.rect, nextRect = nextTransform.rect; bool isFirstBodyNodeLink = (linkDesc.prev.GetComponent <CodeBlock>() && linkDesc.prev.GetComponent <CodeBlock>().FirstBodyNodeObject == linkDesc.next.gameObject) || (linkingNodes && linkingNodeMode == LinkingMode.FirstBodyNode && prevTransform.gameObject == linkingNodesObjects[0] && nextTransform.name == "previewNode"); if (prevTransform.localPosition.x + prevRect.width + connectorPadding < nextTransform.localPosition.x) { line.positionCount = 4 - (isFirstBodyNodeLink ? 1 : 0); int i = 1; if (isFirstBodyNodeLink) { line.SetPosition(i++, new Vector2((prevTransform.localPosition.x), finalPoint.y)); } else { line.SetPosition(i++, new Vector2((nextTransform.localPosition.x - nextRect.width / 2.0f) - connectorPadding, line.GetPosition(0).y)); line.SetPosition(i++, new Vector2((nextTransform.localPosition.x - nextRect.width / 2.0f) - connectorPadding, finalPoint.y)); } // If we're rendering link preview, make it point to the rect center so that it aligns with the cursor if (nextTransform.name == "previewNode") { line.SetPosition(i++, new Vector2(nextTransform.localPosition.x, finalPoint.y)); } else { line.SetPosition(i++, new Vector2((nextTransform.localPosition.x - nextRect.width / 2.0f), finalPoint.y)); } } else { line.positionCount = 6 - (isFirstBodyNodeLink ? 1 : 0); int i = 1; if (isFirstBodyNodeLink) { line.SetPosition(i++, new Vector2((prevTransform.localPosition.x), (prevTransform.localPosition.y + nextTransform.localPosition.y) / 2.0f)); } else { line.SetPosition(i++, new Vector2((prevTransform.localPosition.x + prevRect.width / 2.0f) + connectorPadding, line.GetPosition(0).y)); line.SetPosition(i++, new Vector2((prevTransform.localPosition.x + prevRect.width / 2.0f) + connectorPadding, (prevTransform.localPosition.y + nextTransform.localPosition.y) / 2.0f)); } line.SetPosition(i++, new Vector2((nextTransform.localPosition.x - nextRect.width / 2.0f) - connectorPadding, (prevTransform.localPosition.y + nextTransform.localPosition.y) / 2.0f)); line.SetPosition(i++, new Vector2((nextTransform.localPosition.x - nextRect.width / 2.0f) - connectorPadding, finalPoint.y)); // If we're rendering link preview, make it point to the rect center so that it aligns with the cursor if (nextTransform.name == "previewNode") { line.SetPosition(i++, new Vector2(nextTransform.localPosition.x, finalPoint.y)); } else { line.SetPosition(i++, new Vector2((nextTransform.localPosition.x - nextRect.width / 2.0f), finalPoint.y)); } } } } }
internal bool TryRemoveLinkDescriptor(LinkDescriptor linkDescriptor) { return this.bindings.Remove(linkDescriptor); }
public LinkDescriptorWrapper(LinkDescriptor ed) { this.State = ed.State; this.SourceProperty = ed.SourceProperty; //this.Entity = ed.Entity; }
// Update is called once per frame void Update() { Vector3[] corners = { Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero }; // Get screen-space rectangle of the node rectTransform.GetWorldCorners(corners); Rect nodeRect = new Rect(corners[0], corners[2] - corners[0]); Vector2 pointer = Input.mousePosition; UpdateHUD(pointer, nodeRect); // Drag if LMB held down and inside the node rectangle if (Input.GetKeyDown(KeyCode.Mouse0) && nodeRect.Contains(pointer) || isDragged) { Drag(pointer); } if (Input.GetKeyUp(KeyCode.Mouse0)) { isDragged = false; nodeAlreadyDragged = false; } // Have previewNode follow the mouse // This is used to show a preview of the node connection as the user is moving the mouse to the next/firstBody node if (name == "previewNode") { rectTransform.SetPositionAndRotation(pointer, Quaternion.identity); } // Check if this Draggable isn't part of another node and isn't the previewNode. if (transform.parent == owner.elementContainer.transform && name != "previewNode") { // Linking nodes // coords of the point needed to be right-clicked in order to select a firstBodyNode Vector2 firstBodyHook = new Vector2(nodeRect.xMin + nodeRect.width / 2.0f, nodeRect.yMax); Vector2 nextHook = new Vector2(nodeRect.xMax, nodeRect.yMin + nodeRect.height / 2.0f); if (!owner.linkingNodes && clueHud.hoveredNode == gameObject) { if (Vector2.Distance(pointer, firstBodyHook) < Vector2.Distance(pointer, nextHook)) { clueHud.potentialLinkingMode = EditorProgram.LinkingMode.FirstBodyNode; } else { clueHud.potentialLinkingMode = EditorProgram.LinkingMode.NextNode; } } if (Input.GetKeyUp(KeyCode.Mouse1) && nodeRect.Contains(pointer)) { if (!owner.linkingNodes) { // Make sure we're not trying to link FROM ProgramEnd if (GetComponent <ProgramEnd>() == null) { // Deteremining the right LinkingMode for CodeBlocks if (GetComponent <CodeBlock>() != null) { // Check if it's node A and not node B if (owner.linkingNodesObjects[0] == null) { if (Vector2.Distance(pointer, firstBodyHook) < Vector2.Distance(pointer, nextHook)) { owner.linkingNodeMode = EditorProgram.LinkingMode.FirstBodyNode; } else { owner.linkingNodeMode = EditorProgram.LinkingMode.NextNode; } } } // By default, non-CodeBlocks have NextNode linkingmode else { owner.linkingNodeMode = EditorProgram.LinkingMode.NextNode; } owner.linkingPreviewLine = new GameObject( $"{(owner.linkingNodeMode == EditorProgram.LinkingMode.NextNode ? "nextNode" : "firstBody")}:{gameObject.name}->previewNode", new Type[] { typeof(LineRenderer), typeof(LinkDescriptor) } ); owner.linkingPreviewLine.transform.SetParent(owner.lineCanvas.transform, false); LinkDescriptor linkDesc = owner.linkingPreviewLine.GetComponent <LinkDescriptor>(); linkDesc.prev = gameObject; linkDesc.next = owner.linkingPreviewNode; LineRenderer lineRenderer = owner.linkingPreviewLine.GetComponent <LineRenderer>(); lineRenderer.material = EditorProgram.lineMaterial; lineRenderer.useWorldSpace = false; owner.linkingNodes = true; owner.linkingNodesObjects[0] = gameObject; } } else { owner.linkingNodesObjects[1] = gameObject; owner.LinkCurrentlySelectedObjects(); } } // Other events: Deleting node if (Input.GetKeyDown(KeyCode.Delete) && nodeRect.Contains(pointer) && GetComponent <NodeBase>() && !owner.editingNodeProperty && !GetComponent <ProgramEnd>() && !GetComponent <ProgramStart>()) { clueHud.SetCurrentPrompt(null, null); GetComponent <NodeBase>().DeleteNode(); } if (GetComponent <NodeBase>()) { if (!owner.editingNodeProperty && owner.EditorActive && !GetComponent <ProgramStart>() && !GetComponent <ProgramEnd>() && !owner.choosingFunctionCall && !owner.choosingNode) { if (nodeRect.Contains(pointer)) { if (owner.nodeClipboard) { clueHud.CopyPasteNodePrompt.SetActive(true); clueHud.CopyNodePrompt.SetActive(false); clueHud.PasteNodePrompt.SetActive(false); } else { clueHud.CopyPasteNodePrompt.SetActive(false); clueHud.CopyNodePrompt.SetActive(true); clueHud.PasteNodePrompt.SetActive(false); } } else if (clueHud.hoveredNode == gameObject) { if (owner.nodeClipboard) { clueHud.CopyPasteNodePrompt.SetActive(false); clueHud.CopyNodePrompt.SetActive(false); clueHud.PasteNodePrompt.SetActive(true); } else { clueHud.CopyPasteNodePrompt.SetActive(false); clueHud.CopyNodePrompt.SetActive(false); clueHud.PasteNodePrompt.SetActive(false); } } else if (clueHud.hoveredNode == null) { if (owner.nodeClipboard) { clueHud.CopyPasteNodePrompt.SetActive(false); clueHud.CopyNodePrompt.SetActive(false); clueHud.PasteNodePrompt.SetActive(true); } else { clueHud.CopyPasteNodePrompt.SetActive(false); clueHud.CopyNodePrompt.SetActive(false); clueHud.PasteNodePrompt.SetActive(false); } } } if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) { // Other events: Copying a node if (Input.GetKeyDown(KeyCode.C) && !owner.editingNodeProperty && nodeRect.Contains(pointer) && !GetComponent <ProgramStart>() && !GetComponent <ProgramEnd>()) { owner.nodeClipboard = gameObject; clueHud.CopyPasteNodePrompt.SetActive(true); clueHud.CopyNodePrompt.SetActive(false); clueHud.PasteNodePrompt.SetActive(false); } // Other events: Pasting a node else if (Input.GetKeyDown(KeyCode.V) && !owner.editingNodeProperty && owner.nodeClipboard == gameObject) { GameObject copy = owner.AddNode(owner.nodeClipboard, (pointer.x), pointer.y); NodeBase copyNode = copy.GetComponent <NodeBase>(); copyNode.NextNodeObject = null; copyNode.nextNode = null; copyNode.PrevNodeObject = null; copyNode.prevNode = null; copyNode.ownerLoop = null; if (copyNode.GetComponent <LogicalBlock>()) { copyNode.GetComponent <LogicalBlock>().firstBodyNode = null; copyNode.GetComponent <LogicalBlock>().FirstBodyNodeObject = null; } int guidStartIndex = copy.name.IndexOf("-"); copy.name = copy.name.Replace("(Clone)", ""); copy.name = copy.name.Substring(0, guidStartIndex <= 0 ? copy.name.Length : guidStartIndex) + copy.GetInstanceID(); } } } } // Double-click: usually this is editing a node if (Input.GetKeyUp(KeyCode.Mouse0) && nodeRect.Contains(pointer)) { clickCounter++; timeSinceClick = 0.0f; } if (clickCounter >= 2) { if (transform.parent != owner) { DoubleClick(); } clickCounter = 0; } if (timeSinceClick > doubleClickAllowedTime) { clickCounter = 0; } timeSinceClick += Time.deltaTime; lastFramePointer = pointer; }
/// <summary> /// Remove the link from the list of tracked link descriptors. /// </summary> /// <param name="linkDescriptor">link to be removed.</param> /// <returns>true if the link was tracked and now removed, otherwise returns false.</returns> internal bool TryRemoveLinkDescriptor(LinkDescriptor linkDescriptor) { this.EnsureLinkBindings(); return(this.bindings.Remove(linkDescriptor)); }
internal override void DetachExistingLink(LinkDescriptor existingLink, bool targetDelete) { DetachExistingLinkAction(existingLink, targetDelete); }
internal bool IsRelatedEntity(LinkDescriptor related) { if (this.entity != related.Source) { return (this.entity == related.Target); } return true; }
/// <summary> /// Writes an entity reference link. /// </summary> /// <param name="binding">The link descriptor.</param> /// <param name="requestMessage">The request message used for writing the payload.</param> /// <param name="isBatch">True if batch, false otherwise.</param> internal void WriteEntityReferenceLink(LinkDescriptor binding, ODataRequestMessageWrapper requestMessage, bool isBatch)
/// <summary> /// Validates that the link descriptor source and target have identities. /// </summary> /// <param name="binding">The binding.</param> /// <param name="sourceResource">The source resource.</param> /// <param name="targetResource">The target resource.</param> private static void ValidateLinkDescriptorSourceAndTargetHaveIdentities(LinkDescriptor binding, EntityDescriptor sourceResource, EntityDescriptor targetResource) { Debug.Assert(!binding.ContentGeneratedForSave, "already saved link"); // In non-batch scenarios, the source should always have an identity if (null == sourceResource.GetLatestIdentity()) { binding.ContentGeneratedForSave = true; Debug.Assert(EntityStates.Added == sourceResource.State, "expected added state"); throw Error.InvalidOperation(Strings.Context_LinkResourceInsertFailure, sourceResource.SaveError); } if (null != targetResource && null == targetResource.GetLatestIdentity()) { binding.ContentGeneratedForSave = true; Debug.Assert(EntityStates.Added == targetResource.State, "expected added state"); throw Error.InvalidOperation(Strings.Context_LinkResourceInsertFailure, targetResource.SaveError); } }
internal void WriteEntityReferenceLink(LinkDescriptor binding, ODataRequestMessageWrapper requestMessage) { using (ODataMessageWriter writer = CreateMessageWriter(requestMessage, this.requestInfo)) { Uri resourceUri; EntityDescriptor entityDescriptor = this.requestInfo.EntityTracker.GetEntityDescriptor(binding.Target); if (entityDescriptor.GetLatestIdentity() != null) { resourceUri = entityDescriptor.GetResourceUri(this.requestInfo.BaseUriResolver, false); } else { resourceUri = Util.CreateUri("$" + entityDescriptor.ChangeOrder.ToString(CultureInfo.InvariantCulture), UriKind.Relative); } ODataEntityReferenceLink link = new ODataEntityReferenceLink { Url = resourceUri }; writer.WriteEntityReferenceLink(link); } }
internal bool IsRelatedEntity(LinkDescriptor related) { return this.entity == related.Source || this.entity == related.Target; }
/// <summary>Applies all accumulated changes to the associated data context.</summary> /// <remarks>The log should be cleared after this method successfully executed.</remarks> internal void ApplyToContext() { if (!this.Tracking) { return; } foreach (KeyValuePair <Uri, ODataResource> entity in this.identityStack) { // Try to attach the entity descriptor got from materializer, if one already exists, get the existing reference instead. MaterializerEntry entry = MaterializerEntry.GetEntry(entity.Value); bool mergeEntityDescriptorInfo = entry.CreatedByMaterializer || entry.ResolvedObject == this.insertRefreshObject || entry.ShouldUpdateFromPayload; // Whenever we merge the data, only at those times will be merge the links also EntityDescriptor descriptor = this.entityTracker.InternalAttachEntityDescriptor(entry.EntityDescriptor, false /*failIfDuplicated*/); AtomMaterializerLog.MergeEntityDescriptorInfo(descriptor, entry.EntityDescriptor, mergeEntityDescriptorInfo, this.mergeOption); if (mergeEntityDescriptorInfo) { // In AtomMaterializer.TryResolveFromContext, we set AtomEntry.ShouldUpdateFromPayload to true // when even MergeOption is PreserveChanges and entityState is Deleted. But in that case, we cannot // set the entity state to Unchanged, hence need to workaround that one scenario if (this.mergeOption != MergeOption.PreserveChanges || descriptor.State != EntityStates.Deleted) { // we should always reset descriptor's state to Unchanged (old v1 behavior) descriptor.State = EntityStates.Unchanged; descriptor.PropertiesToSerialize.Clear(); } } } foreach (LinkDescriptor link in this.links) { if (EntityStates.Added == link.State) { // Added implies collection this.entityTracker.AttachLink(link.Source, link.SourceProperty, link.Target, this.mergeOption); } else if (EntityStates.Modified == link.State) { // Modified implies reference object target = link.Target; if (MergeOption.PreserveChanges == this.mergeOption) { // GetLinks looks up the existing link using just the SourceProperty, the declaring server type name is not significant here. LinkDescriptor end = this.entityTracker.GetLinks(link.Source, link.SourceProperty).SingleOrDefault(); if (end != null && end.Target == null) { // leave the SetLink(link.Source, link.SourceProperty, null) continue; } if ((target != null) && (this.entityTracker.GetEntityDescriptor(target).State == EntityStates.Deleted) || (EntityStates.Deleted == this.entityTracker.GetEntityDescriptor(link.Source).State)) { target = null; } } this.entityTracker.AttachLink(link.Source, link.SourceProperty, target, this.mergeOption); } else { // detach link Debug.Assert(EntityStates.Detached == link.State, "not detached link"); this.entityTracker.DetachExistingLink(link, false); } } }
private static void HandleResponsePost(LinkDescriptor linkDescriptor) { if ((EntityStates.Added != linkDescriptor.State) && ((EntityStates.Modified != linkDescriptor.State) || (linkDescriptor.Target == null))) { System.Data.Services.Client.Error.ThrowBatchUnexpectedContent(InternalError.LinkNotAddedState); } linkDescriptor.State = EntityStates.Unchanged; }
/// <summary> /// Invoke this method to notify the log that a link was set on /// a property. /// </summary> /// <param name="source">Entry for source object.</param> /// <param name="propertyName">Name of property set.</param> /// <param name="target">Target object.</param> internal void SetLink(MaterializerEntry source, string propertyName, object target) { Debug.Assert(source.Entry != null || source.ForLoadProperty, "source != null || source.ForLoadProperty"); Debug.Assert(propertyName != null, "propertyName != null"); if (!this.Tracking) { return; } if (IsEntity(source) && IsEntity(target, this.clientEdmModel)) { Debug.Assert(this.Tracking, "this.Tracking -- otherwise there's an 'if' missing (it happens to be that the assert holds for all current callers"); LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Modified); this.links.Add(item); } }
/// <summary>is the entity the same as the source or target entity</summary> /// <param name="related">related end</param> /// <returns>true if same as source or target entity</returns> internal bool IsRelatedEntity(LinkDescriptor related) { return((this.entity == related.Source) || (this.entity == related.Target)); }
public void SerializeEnity_EnumProperty() { MyEntity1 myEntity1 = new MyEntity1() { ID = 2, MyColorValue = MyColor.Yellow, MyFlagsColorValue = MyFlagsColor.Blue, ComplexValue1Value = new ComplexValue1() { MyColorValue = MyColor.Green, MyFlagsColorValue = MyFlagsColor.Red }, MyFlagsColorCollection1 = new List <MyFlagsColor>() { MyFlagsColor.Blue, MyFlagsColor.Red, MyFlagsColor.Red }, MyColorCollection = new List <MyColor?>() }; DataServiceContext dataServiceContext = new DataServiceContext(new Uri("http://www.odata.org/service.svc")); dataServiceContext.EnableAtom = true; dataServiceContext.Format.UseAtom(); dataServiceContext.AttachTo("MyEntitySet1", myEntity1); var requestInfo = new RequestInfo(dataServiceContext); var serializer = new Serializer(requestInfo); var headers = new HeaderCollection(); headers.SetHeader("Content-Type", "application/atom+xml;odata.metadata=minimal"); var clientModel = new ClientEdmModel(ODataProtocolVersion.V4); var entityDescriptor = new EntityDescriptor(clientModel); entityDescriptor.State = EntityStates.Added; entityDescriptor.Entity = myEntity1; var requestMessageArgs = new BuildingRequestEventArgs("POST", new Uri("http://www.foo.com/Northwind"), headers, entityDescriptor, HttpStack.Auto); var linkDescriptors = new LinkDescriptor[] { }; var odataRequestMessageWrapper = ODataRequestMessageWrapper.CreateRequestMessageWrapper(requestMessageArgs, requestInfo); serializer.WriteEntry(entityDescriptor, linkDescriptors, odataRequestMessageWrapper); // read result: MemoryStream stream = (MemoryStream)(odataRequestMessageWrapper.CachedRequestStream.Stream); stream.Position = 0; string payload = (new StreamReader(stream)).ReadToEnd(); payload = Regex.Replace(payload, "<updated>[^<]*</updated>", ""); payload.Should().Be( "<?xml version=\"1.0\" encoding=\"utf-8\"?><entry xmlns=\"http://www.w3.org/2005/Atom\" " + "xmlns:d=\"http://docs.oasis-open.org/odata/ns/data\" xmlns:m=\"http://docs.oasis-open.org/odata/ns/metadata\" " + "xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\">" + "<id />" + "<title />" + //"<updated>2013-11-11T19:29:54Z</updated>" + "<author><name /></author>" + "<content type=\"application/xml\">" + "<m:properties>" + "<d:ComplexValue1Value>" + "<d:MyColorValue m:type=\"#AstoriaUnitTests.TDD.Tests.Client.ODataWriterWrapperUnitTests_MyColor\">Green</d:MyColorValue>" + "<d:MyFlagsColorValue m:type=\"#AstoriaUnitTests.TDD.Tests.Client.ODataWriterWrapperUnitTests_MyFlagsColor\">Red</d:MyFlagsColorValue>" + "<d:StringValue m:null=\"true\" />" + "</d:ComplexValue1Value>" + "<d:ID m:type=\"Int64\">2</d:ID>" + "<d:MyColorCollection />" + "<d:MyColorValue m:type=\"#AstoriaUnitTests.TDD.Tests.Client.ODataWriterWrapperUnitTests_MyColor\">Yellow</d:MyColorValue>" + "<d:MyFlagsColorCollection1>" + "<m:element m:type=\"#AstoriaUnitTests.TDD.Tests.Client.ODataWriterWrapperUnitTests+MyFlagsColor\">Blue</m:element>" + "<m:element m:type=\"#AstoriaUnitTests.TDD.Tests.Client.ODataWriterWrapperUnitTests+MyFlagsColor\">Red</m:element>" + "<m:element m:type=\"#AstoriaUnitTests.TDD.Tests.Client.ODataWriterWrapperUnitTests+MyFlagsColor\">Red</m:element>" + "</d:MyFlagsColorCollection1>" + "<d:MyFlagsColorValue m:type=\"#AstoriaUnitTests.TDD.Tests.Client.ODataWriterWrapperUnitTests_MyFlagsColor\">Blue</d:MyFlagsColorValue>" + "</m:properties>" + "</content>" + "</entry>"); }
/// <summary> /// attach the link with the given source, sourceProperty and target. /// </summary> /// <param name="source">source entity of the link.</param> /// <param name="sourceProperty">name of the property on the source entity.</param> /// <param name="target">target entity of the link.</param> /// <param name="linkMerge">merge option to be used to merge the link if there is an existing link.</param> internal override void AttachLink(object source, string sourceProperty, object target, MergeOption linkMerge) { LinkDescriptor relation = new LinkDescriptor(source, sourceProperty, target, this.model); LinkDescriptor existing = this.TryGetLinkDescriptor(source, sourceProperty, target); if (existing != null) { switch (linkMerge) { case MergeOption.AppendOnly: break; case MergeOption.OverwriteChanges: relation = existing; break; case MergeOption.PreserveChanges: if ((EntityStates.Added == existing.State) || (EntityStates.Unchanged == existing.State) || (EntityStates.Modified == existing.State && null != existing.Target)) { relation = existing; } break; case MergeOption.NoTracking: // public API point should throw if link exists throw Error.InvalidOperation(Strings.Context_RelationAlreadyContained); } } else { if (this.model.GetClientTypeAnnotation(this.model.GetOrCreateEdmType(source.GetType())).GetProperty(sourceProperty, false).IsEntityCollection || (null == (existing = this.DetachReferenceLink(source, sourceProperty, target, linkMerge)))) { this.AddLink(relation); this.IncrementChange(relation); } else if (!((MergeOption.AppendOnly == linkMerge) || (MergeOption.PreserveChanges == linkMerge && EntityStates.Modified == existing.State))) { // AppendOnly doesn't change state or target // OverWriteChanges changes target and state // PreserveChanges changes target if unchanged, leaves modified target and state alone relation = existing; } } relation.State = EntityStates.Unchanged; }
protected static string GetLinkHttpMethod(LinkDescriptor link) { if (!link.IsSourcePropertyCollection) { if (link.Target == null) { return "DELETE"; } return "PUT"; } if (EntityStates.Deleted == link.State) { return "DELETE"; } return "POST"; }
/// <summary> /// Remove the link from the list of tracked link descriptors. /// </summary> /// <param name="linkDescriptor">link to be removed.</param> /// <returns>true if the link was tracked and now removed, otherwise returns false.</returns> internal bool TryRemoveLinkDescriptor(LinkDescriptor linkDescriptor) { this.EnsureLinkBindings(); return this.bindings.Remove(linkDescriptor); }
internal bool IsRelatedEntity(LinkDescriptor related) { return ((this.entity == related.Source) || (this.entity == related.Target)); }
/// <summary>Handle changeset response.</summary> /// <param name="linkDescriptor">headers of changeset response</param> private static void HandleResponsePost(LinkDescriptor linkDescriptor) { if (!((EntityStates.Added == linkDescriptor.State) || (EntityStates.Modified == linkDescriptor.State && null != linkDescriptor.Target))) { Error.ThrowBatchUnexpectedContent(InternalError.LinkNotAddedState); } linkDescriptor.State = EntityStates.Unchanged; }
/// <summary>Detach existing link</summary> /// <param name="existingLink">link to detach</param> /// <param name="targetDelete">true if target is being deleted, false otherwise</param> internal abstract void DetachExistingLink(LinkDescriptor existingLink, bool targetDelete);
/// <summary> /// Invoke this method to notify the log that a new link was /// added to a collection. /// </summary> /// <param name="source"> /// Instance with the collection to which <paramref name="target"/> /// was added. /// </param> /// <param name="propertyName">Property name for collection.</param> /// <param name="target">Object which was added.</param> internal void AddedLink(AtomEntry source, string propertyName, object target) { Debug.Assert(source != null, "source != null"); Debug.Assert(propertyName != null, "propertyName != null"); if (!this.Tracking) { return; } if (ShouldTrackWithContext(source) && ShouldTrackWithContext(target)) { LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Added); this.links.Add(item); } }
internal void DetachExistingLink(LinkDescriptor existingLink, bool targetDelete) { if (existingLink.Target != null) { EntityDescriptor entityDescriptor = this.GetEntityDescriptor(existingLink.Target); if (entityDescriptor.IsDeepInsert && !targetDelete) { EntityDescriptor parentForInsert = entityDescriptor.ParentForInsert; if (object.ReferenceEquals(entityDescriptor.ParentEntity, existingLink.Source) && ((parentForInsert.State != EntityStates.Deleted) || (parentForInsert.State != EntityStates.Detached))) { throw new InvalidOperationException(System.Data.Services.Client.Strings.Context_ChildResourceExists); } } } if (this.TryRemoveLinkDescriptor(existingLink)) { existingLink.State = EntityStates.Detached; } }
/// <summary> /// Invoke this method to notify the log that a link was set on /// a property. /// </summary> /// <param name="source">Entry for source object.</param> /// <param name="propertyName">Name of property set.</param> /// <param name="target">Target object.</param> internal void SetLink(AtomEntry source, string propertyName, object target) { Debug.Assert(source != null, "source != null"); Debug.Assert(propertyName != null, "propertyName != null"); if (!this.Tracking) { return; } if (ShouldTrackWithContext(source) && ShouldTrackWithContext(target)) { Debug.Assert(this.Tracking, "this.Tracking -- otherwise there's an 'if' missing (it happens to be that the assert holds for all current callers"); LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Modified); this.links.Add(item); } }
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> /// Generate a request for the given link. /// </summary> /// <param name="binding">Instance of LinkDescriptor.</param> /// <param name="requestMessage">Instance of IODataRequestMessage to be used to generate the payload.</param> private void CreateRequestData(LinkDescriptor binding, ODataRequestMessageWrapper requestMessage) { Debug.Assert( (binding.State == EntityStates.Added) || (binding.State == EntityStates.Modified && null != binding.Target), "This method must be called only when a binding is added or put"); #if DEBUG Debug.Assert(!Util.IsBatchWithSingleChangeset(this.Options) || this.IsBatchRequest, "If this.Options.IsBatchWithSingleChangeset() is true, this.IsBatchRequest must also be true."); this.SerializerInstance.WriteEntityReferenceLink(binding, requestMessage, Util.IsBatchWithSingleChangeset(this.Options)); #else this.SerializerInstance.WriteEntityReferenceLink(binding, requestMessage); #endif }
public static DataServiceResponse SaveChanges(DataServiceContext context, SaveChangesOptions options, SaveChangesMode saveMode) { IList <EntityDescriptor> entites = (from e in context.Entities where e.State != EntityStates.Unchanged select e).ToArray(); IList <LinkDescriptor> links = (from e in context.Links where e.State != EntityStates.Unchanged select e).ToArray(); IList <StreamDescriptor> streams = context.Entities.SelectMany(e => e.StreamDescriptors).Where(s => s.State != EntityStates.Unchanged).ToArray(); IList <EntityDescriptor> deletedEntities = (from e in entites where e.State == EntityStates.Deleted select e).ToArray(); IList <LinkDescriptor> deletedLinks = (from e in links where e.State == EntityStates.Deleted select e).ToArray(); DataServiceResponse response = null; switch (saveMode) { case SaveChangesMode.Synchronous: response = context.SaveChanges(options); break; case SaveChangesMode.AsyncWaitOnAsyncWaitHandle: { IAsyncResult async = context.BeginSaveChanges(options, null, null); if (!async.CompletedSynchronously) { Assert.IsTrue(async.AsyncWaitHandle.WaitOne(new TimeSpan(0, 0, TestConstants.MaxTestTimeout), false), "BeginSaveChanges {0} timeout", options); } Assert.IsTrue(async.IsCompleted); response = context.EndSaveChanges(async); break; } case SaveChangesMode.AsyncCallback: { SaveChangesCallback callback = new SaveChangesCallback(); IAsyncResult async = context.BeginSaveChanges(options, callback.CallbackMethod, new object[] { options, context }); Assert.IsTrue(callback.Finished.WaitOne(new TimeSpan(0, 0, TestConstants.MaxTestTimeout), false), "BeginSaveChanges {0} Asyncallback timeout", options); Assert.IsTrue(async.IsCompleted); if (null != callback.CallbackFailure) { Assert.IsNull(callback.CallbackResult, callback.CallbackFailure.ToString()); throw callback.CallbackFailure; } response = (DataServiceResponse)callback.CallbackResult; break; } default: Assert.Fail("shouldn't be here"); break; } int entityIndex = 0; int linkIndex = 0; int streamIndex = 0; if (options == SaveChangesOptions.BatchWithSingleChangeset) { Assert.AreEqual <int>(response.BatchStatusCode, (int)HttpStatusCode.Accepted, "Expecting 202 as the status code for batch requests"); Assert.IsTrue(response.BatchHeaders["Content-Type"].StartsWith("multipart/mixed; boundary=batchresponse_"), "expecting content type to be multipart mixed with a boundary value"); Assert.IsTrue(response.IsBatchResponse, "Expecting response to be batch response"); } else { Assert.AreEqual <int>(response.BatchStatusCode, -1, "expecting status code to be zero"); Assert.IsTrue(response.BatchHeaders.Count == 0, "expecting no header information"); Assert.IsFalse(response.IsBatchResponse, "expecting this to be non batch response"); } foreach (ChangeOperationResponse changeset in response) { EntityStates state; bool wasDeletedState; if (changeset.Descriptor is EntityDescriptor) { EntityDescriptor tor = (EntityDescriptor)changeset.Descriptor; state = tor.State; wasDeletedState = deletedEntities.Contains(tor); // for MLE, more than one request can be sent for the same entity descriptor if (entites.Count > entityIndex && Object.ReferenceEquals(entites[entityIndex].Entity, tor.Entity)) { entityIndex++; } else { Assert.IsTrue(Object.ReferenceEquals(tor.Entity, entites[entityIndex - 1].Entity), "For MLE, it must match with the previous request"); } Assert.IsNull(changeset.Error); } else if (changeset.Descriptor is LinkDescriptor) { LinkDescriptor tor = (LinkDescriptor)changeset.Descriptor; state = tor.State; wasDeletedState = deletedLinks.Contains(tor); Assert.AreSame(tor.Source, links[linkIndex].Source); Assert.AreEqual(tor.SourceProperty, links[linkIndex].SourceProperty); Assert.AreSame(tor.Target, links[linkIndex].Target); Assert.IsNull(changeset.Error); linkIndex++; } else { Assert.IsTrue(changeset.Descriptor is StreamDescriptor, "Must be stream descriptor"); if (streams.Count > streamIndex && streams.Contains(changeset.Descriptor)) { streamIndex++; } state = changeset.Descriptor.State; wasDeletedState = false; } if (changeset.Error != null) { Assert.AreNotEqual(EntityStates.Unchanged, state); } else { if (wasDeletedState) { Assert.AreEqual(EntityStates.Detached, state); } else { Assert.AreEqual(EntityStates.Unchanged, state); } } } Assert.AreEqual(entites.Count, entityIndex, "entities SaveChangesOptions.{0}", options); Assert.AreEqual(links.Count, linkIndex, "links SaveChangesOptions.{0}", options); Assert.AreEqual(streams.Count, streamIndex, "streams SaveChangesOptions.{0}", options); entites = context.Entities; links = context.Links; return(response); }
/// <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; }
/// <summary>Get the value of HttpMethod enum from link resource state</summary> /// <param name="link">Instance of LinkDescriptor containing the link state and type of link.</param> /// <returns>HttpMethod enum value for the link descriptor state.</returns> protected static string GetLinkHttpMethod(LinkDescriptor link) { if (!link.IsSourcePropertyCollection) { Debug.Assert(EntityStates.Modified == link.State, "not Modified state"); if (null == link.Target) { // REMOVE/DELETE a reference return XmlConstants.HttpMethodDelete; } else { // UPDATE/PUT a reference return XmlConstants.HttpMethodPut; } } else if (EntityStates.Deleted == link.State) { // you call DELETE on $ref return XmlConstants.HttpMethodDelete; } else { // you INSERT/POST into a collection Debug.Assert(EntityStates.Added == link.State, "not Added state"); return XmlConstants.HttpMethodPost; } }
/// <summary> /// Generate the link payload. /// </summary> /// <param name="binding">binding</param> /// <returns>An instance of ODataRequestMessage for the link request.</returns> protected ODataRequestMessageWrapper CreateRequest(LinkDescriptor binding) { Debug.Assert(null != binding, "null binding"); if (binding.ContentGeneratedForSave) { return null; } EntityDescriptor sourceEntityDescriptor = this.RequestInfo.EntityTracker.GetEntityDescriptor(binding.Source); EntityDescriptor targetEntityDescriptor = (null != binding.Target) ? this.RequestInfo.EntityTracker.GetEntityDescriptor(binding.Target) : null; // We allow the source and target to be in Added state, i.e. without identities, for batch with single changeset. if (!Util.IsBatchWithSingleChangeset(this.Options)) { ValidateLinkDescriptorSourceAndTargetHaveIdentities(binding, sourceEntityDescriptor, targetEntityDescriptor); } Debug.Assert(this.IsBatchRequest || null != sourceEntityDescriptor.GetLatestIdentity(), "missing sourceResource.Identity in non-batch"); Uri requestUri = null; LinkInfo linkInfo = null; if (sourceEntityDescriptor.TryGetLinkInfo(binding.SourceProperty, out linkInfo) && linkInfo.AssociationLink != null) { Debug.Assert(null != sourceEntityDescriptor.GetLatestIdentity(), "Source must have an identity in order to have link info"); // If there is already an Association link from the payload, use that requestUri = linkInfo.AssociationLink; } else { Uri sourceEntityUri; if (null == sourceEntityDescriptor.GetLatestIdentity()) { Debug.Assert(this.IsBatchRequest && Util.IsBatchWithSingleChangeset(this.Options), "Source must have an identity outside of batch with single changeset"); // if the source hasn't yet been inserted (because its in batch), then create a uri based on its content-ID sourceEntityUri = UriUtil.CreateUri("$" + sourceEntityDescriptor.ChangeOrder.ToString(CultureInfo.InvariantCulture), UriKind.Relative); } else { // otherwise use the edit link of the source sourceEntityUri = sourceEntityDescriptor.GetResourceUri(this.RequestInfo.BaseUriResolver, false /*queryLink*/); } // get the source property Uri string sourcePropertyUri = GetSourcePropertyUri(binding, sourceEntityDescriptor); // get the convention-based relative uri for the association Uri conventionalRelativeUri = UriUtil.CreateUri(sourcePropertyUri, UriKind.Relative); // add $ref at the end conventionalRelativeUri = UriUtil.CreateUri(UriUtil.UriToString(conventionalRelativeUri) + UriHelper.FORWARDSLASH + XmlConstants.UriLinkSegment, UriKind.Relative); // combine the association uri with the source entity uri requestUri = UriUtil.CreateUri(sourceEntityUri, conventionalRelativeUri); } // in the case of deleting a link from a collection, the key of the target must be appended requestUri = AppendTargetEntityKeyIfNeeded(requestUri, binding, targetEntityDescriptor); string method = GetLinkHttpMethod(binding); HeaderCollection headers = new HeaderCollection(); headers.SetRequestVersion(Util.ODataVersion4, this.RequestInfo.MaxProtocolVersionAsVersion); this.RequestInfo.Format.SetRequestAcceptHeader(headers); // if (EntityStates.Deleted || (EntityState.Modifed && null == TargetResource)) // then the server will fail the batch section if content type exists if ((EntityStates.Added == binding.State) || (EntityStates.Modified == binding.State && (null != binding.Target))) { this.RequestInfo.Format.SetRequestContentTypeForLinks(headers); } return this.CreateRequestMessage(method, requestUri, headers, this.RequestInfo.HttpStack, binding, this.IsBatchRequest ? binding.ChangeOrder.ToString(CultureInfo.InvariantCulture) : null); }