public void DoWork(IRequest request) { string resourceKind = _requestContext.ResourceKind.ToString(); string endpoint = _requestContext.DatasetLink + resourceKind; SdataContext sdataContext = _requestContext.SdataContext; ISyncSyncDigestInfoStore syncDigestStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetSyncDigestStore(sdataContext); SyncDigestInfo syncDigestInfo = syncDigestStore.Get(resourceKind); SyncDigestPayload syncDigestPayload = new SyncDigestPayload(); syncDigestPayload.Digest.Origin = endpoint; // create a new initial syncDigest and store it. if ((null == syncDigestInfo) || (syncDigestInfo.Count == 0)) { SyncFeedDigestEntry entry = new SyncFeedDigestEntry(); entry.ConflictPriority = 0; entry.Endpoint = endpoint; entry.Stamp = DateTime.Now; entry.Tick = -1; syncDigestPayload.Digest.Entries.Add(entry); } else { foreach (SyncDigestEntryInfo digestEntry in syncDigestInfo) { SyncFeedDigestEntry entry = new SyncFeedDigestEntry(); entry.ConflictPriority = digestEntry.ConflictPriority; entry.Endpoint = digestEntry.Endpoint; //entry.Stamp = digestEntry.; entry.Tick = digestEntry.Tick; syncDigestPayload.Digest.Entries.Add(entry); } } SyncFeed feed = new SyncFeed(); string url = endpoint + "/$syncDigest"; FeedLink link = new FeedLink(url, LinkType.Self, MediaType.AtomEntry); feed.Links = new FeedLinkCollection(); feed.Links.Add(link); feed.Id = url; feed.FeedType = FeedType.ResourceEntry; SyncFeedEntry syncFeedEntry = new SyncFeedEntry(); syncFeedEntry.Payload = syncDigestPayload; syncFeedEntry.Title = "Synchronization digest"; syncFeedEntry.Id = url; syncFeedEntry.Links.Add(link); feed.Entries.Add(syncFeedEntry); feed.Id = url; request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = feed; request.Response.ContentType = MediaType.AtomEntry; }
public void DoWork(IRequest request) { // only atom entry supported!!! if (request.ContentType != Sage.Common.Syndication.MediaType.AtomEntry) throw new RequestException("Atom entry content type expected"); // deserialize the request stream to a SyncFeedEntry SyncFeedEntry entry = new SyncFeedEntry(); XmlReader reader = XmlReader.Create(request.Stream); reader.MoveToContent(); entry.ReadXml(reader, typeof(SyncDigestPayload)); if (null == entry.Linked) throw new RequestException("Invalid content: element 'linked' missing"); // Parse the resource url to get the local id. // Additionally we check for equality of the urls of the request url and // in the linked element up to the resourceKind. string requestEndpointUrl = _requestContext.OriginEndPoint; RequestContext entryResourceAsRequestContext = new RequestContext(new Sage.Common.Syndication.SDataUri(entry.Linked.Resource)); // TODO: not really nice here. string linkedResourceUrl = entryResourceAsRequestContext.OriginEndPoint; if (!string.Equals(requestEndpointUrl, linkedResourceUrl, StringComparison.InvariantCultureIgnoreCase)) throw new RequestException("Request url and linked entry resource not matching."); string resourceKindName = _requestContext.ResourceKind.ToString(); string localId = entryResourceAsRequestContext.SdataUri.CollectionPredicate; Guid uuid = entry.Linked.Uuid; // store a new correlation entry to the sync store ICorrelatedResSyncInfoStore correlatedResSyncInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_requestContext.SdataContext); ResSyncInfo newResSyncInfo = new ResSyncInfo(uuid, requestEndpointUrl, 0, string.Empty, DateTime.Now); CorrelatedResSyncInfo newInfo = new CorrelatedResSyncInfo(localId, newResSyncInfo); correlatedResSyncInfoStore.Add(resourceKindName, newInfo); // Set the response values SyncFeed feed = new SyncFeed(); feed.FeedType = FeedType.LinkedSingle; SyncFeedEntry feedEntry = new SyncFeedEntry(); feedEntry.Title = FeedMetadataHelpers.BuildLinkedEntryTitle(_requestContext, uuid); feedEntry.Id = FeedMetadataHelpers.BuildLinkedEntryUrl(_requestContext, uuid); feedEntry.Updated = newInfo.ResSyncInfo.ModifiedStamp; feedEntry.Published = newInfo.ResSyncInfo.ModifiedStamp; feedEntry.Linked = entry.Linked; feed.Entries.Add(feedEntry); request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = (IFeed)feed; request.Response.ContentType = MediaType.AtomEntry; request.Response.StatusCode = System.Net.HttpStatusCode.Created; request.Response.Protocol.SendUnknownResponseHeader("location", FeedMetadataHelpers.BuildLinkedEntryUrl(_requestContext, uuid)); }
public void DoWork(IRequest request) { if (request.ContentType != Sage.Common.Syndication.MediaType.AtomEntry) throw new RequestException("Atom entry content type expected"); if (!String.IsNullOrEmpty(_requestContext.ResourceKey)) throw new RequestException("Please use resource url"); // read entry from stream SyncFeedEntry entry = new SyncFeedEntry(); XmlReader reader = XmlReader.Create(request.Stream); reader.MoveToContent(); entry.ReadXml(reader, ResourceKindHelpers.GetPayloadType(_requestContext.ResourceKind)); IEntityWrapper wrapper = EntityWrapperFactory.Create(_requestContext.ResourceKind, _requestContext); Document document = wrapper.GetTransformedDocument(entry.Payload, entry.SyncLinks); if (!String.IsNullOrEmpty(document.Id)) throw new RequestException("Entity alredy exists"); // Add Document SdataTransactionResult sdTrResult = wrapper.Add(entry.Payload, entry.SyncLinks ); if (sdTrResult == null) throw new RequestException("Entity does not exists"); //????? if ((sdTrResult.HttpStatus == System.Net.HttpStatusCode.OK) || (sdTrResult.HttpStatus == System.Net.HttpStatusCode.Created)) { // store correlation SyncFeedEntry responseEntry = wrapper.GetFeedEntry(sdTrResult.LocalId); SyncFeed feed = new SyncFeed(); feed.FeedType = FeedType.ResourceEntry; feed.Entries.Add(responseEntry); request.Response.StatusCode = System.Net.HttpStatusCode.Created; request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = feed; request.Response.Protocol.SendUnknownResponseHeader("Location", responseEntry.Id); request.Response.ContentType = Sage.Common.Syndication.MediaType.AtomEntry; } else { throw new RequestException(sdTrResult.HttpMessage); } }
public void DoWork(IRequest request) { if (request.ContentType!= Sage.Common.Syndication.MediaType.AtomEntry) throw new RequestException("Atom entry content type expected"); if (String.IsNullOrEmpty(_requestContext.ResourceKey)) throw new RequestException("Please use single resource url"); //SupportedResourceKinds resKind = _requestContext.ResourceKind; //switch (resKind) //{ // case SupportedResourceKinds.tradingAccounts: // case SupportedResourceKinds.contacts: // case SupportedResourceKinds.phoneNumbers: // case SupportedResourceKinds.postalAddresses: // break; // default: // throw new RequestException("Put is not Supported for requested resource"); //} // read entry from stream SyncFeedEntry entry = new SyncFeedEntry(); XmlReader reader = XmlReader.Create(request.Stream); reader.MoveToContent(); entry.ReadXml(reader, ResourceKindHelpers.GetPayloadType(_requestContext.ResourceKind)); IEntityWrapper wrapper = EntityWrapperFactory.Create(_requestContext.ResourceKind, _requestContext); Token emptyToken = new Token(); Identity identity = wrapper.GetIdentity(_requestContext.ResourceKey); Document originalDocument = wrapper.Entity.GetDocument(identity, emptyToken, _requestContext.Config); if (originalDocument.LogState == LogState.Deleted) throw new RequestException("Entity does not exists"); entry.Payload.LocalID = _requestContext.ResourceKey; SdataTransactionResult sdTrResult = wrapper.Update(entry.Payload, entry.SyncLinks); if (sdTrResult == null) { SyncFeedEntry responseEntry = wrapper.GetFeedEntry(_requestContext.ResourceKey); SyncFeed feed = new SyncFeed(); feed.FeedType = FeedType.ResourceEntry; feed.Entries.Add(responseEntry); request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = feed; request.Response.ContentType = Sage.Common.Syndication.MediaType.AtomEntry; } else if (sdTrResult.HttpStatus == System.Net.HttpStatusCode.OK) { SyncFeedEntry responseEntry = wrapper.GetFeedEntry(_requestContext.ResourceKey); SyncFeed feed = new SyncFeed(); feed.FeedType = FeedType.ResourceEntry; feed.Entries.Add(responseEntry); request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = feed; request.Response.ContentType = Sage.Common.Syndication.MediaType.AtomEntry; } else { throw new RequestException(sdTrResult.HttpMessage); } }
public override SyncFeedEntry GetFeedEntry(string idString) { #region declarations int recordCount; DataSets.Order order = new DataSets.Order(); CalculatedOrdersTableAdapter tableAdapter; tableAdapter = new CalculatedOrdersTableAdapter(); CalculatedOrderDetailsTableAdapter detailTableAdapter; detailTableAdapter = new CalculatedOrderDetailsTableAdapter(); //DeletedOrderDetailsTableAdapter deletedDetailTableAdapter; //deletedDetailTableAdapter = new DeletedOrderDetailsTableAdapter(); int id; if (!(Int32.TryParse(idString, out id))) id = 0; #endregion #region fill dataset using (OleDbConnection connection = new OleDbConnection(_context.Config.ConnectionString)) { tableAdapter.Connection = connection; recordCount = tableAdapter.FillBy(order.CalculatedOrders, id); if (recordCount == 0) return null; detailTableAdapter.Connection = connection; detailTableAdapter.FillBy(order.CalculatedOrderDetails, id); //deletedDetailTableAdapter.Connection = connection; //deletedDetailTableAdapter.Fill(order.DeletedOrderDetails, id.ToString(), lastToken.SequenceNumber, config.CrmUser); } #endregion SyncFeedEntry entry = new SyncFeedEntry(); entry.Id = String.Format("{0}{1}('{2}')", _context.DatasetLink, _resourceKind.ToString(), idString); entry.Title = String.Format("{0}: {1}", _resourceKind.ToString(), idString); entry.Updated = DateTime.Now; entry.Payload = GetPayload((DataSets.Order.CalculatedOrdersRow)order.CalculatedOrders[0], order.CalculatedOrderDetails, //order.DeletedOrderDetails, _context.Config); entry.SyncLinks.AddRange(GetLinks(entry.Payload.ForeignIds)); return entry; }
public void DoWork(IRequest request) { // If an asyncState object already exists, an exception is thrown as the performer only accepts // one call after each other. The Request receiver has to manage a queued execution. // Before calling the performers execution implementation method a new AsyncState object is created // and set to an initial tracking state. lock (_asyncStateObj) { if (null != _asyncStateObj.Tracking) throw new InvalidOperationException("The performer cannot be executed because it is already running."); SyncTracking tracking = new SyncTracking(); tracking.ElapsedSeconds = 1; tracking.Phase = TrackingPhase.INIT; tracking.PhaseDetail = "Tracking Id was: " + _requestContext.TrackingId.ToString(); tracking.PollingMillis = 100; tracking.RemainingSeconds = 100; _asyncStateObj.Tracking = tracking; } // *** Initialization for the async execution *** // - Read SyncDigest from request stream. // - Read trackingId from request URL //// Load input stream (throws RequestException if it fails.) //XmlDocument xmlInputDoc; //InputStreamHelpers.Get(request, out xmlInputDoc); //// load syncDigest info from input stream of the request //SyncDigestInfo syncDigestInfo = SyncDigestInfoHelpers.Load(xmlInputDoc); SyncFeedEntry entry = new SyncFeedEntry(); XmlReader reader = XmlReader.Create(request.Stream); reader.MoveToContent(); entry.ReadXml(reader, typeof(SyncDigestPayload)); if (entry.Payload == null) { throw new RequestException("Invalid Content"); } SyncFeedDigest syncDigestInfo = ((SyncDigestPayload)entry.Payload).Digest; // convert tracking ID from request to type Guid string strTrackingId = request.Uri.TrackingID; if (String.IsNullOrEmpty(strTrackingId)) throw new RequestException("TrackingId is missing"); GuidConverter converter = new GuidConverter(); this.TrackingId = (Guid)converter.ConvertFrom(strTrackingId); // *** Do work asynchronously *** _asyncPerformer = new InternalAsyncPerformer(this); _asyncPerformer.DoWork(_requestContext.Config, syncDigestInfo); // *** set the tracking to the request response *** this.GetTrackingState(request); }
/// <summary> /// /// </summary> /// <param name="config"></param> /// <returns></returns> /// <remarks>This method is not threadsafe as the performer must be finished when calling this method.</remarks> public SyncFeed GetFeed(NorthwindConfig config, int startIndex, int count) { SyncFeed feed; SdataContext sdataContext; SupportedResourceKinds resource; string resourceKind; string endpoint; Guid trackingId; List<CorrelatedResSyncInfo> correlatedResSyncInfos; sdataContext = _parentPerformer._requestContext.SdataContext; resource = _parentPerformer._requestContext.ResourceKind; resourceKind = resource.ToString(); correlatedResSyncInfos = _parentPerformer._asyncStateObj.CorrelatedResSyncInfos; endpoint = _parentPerformer._requestContext.DatasetLink + resourceKind; trackingId = _parentPerformer._requestContext.TrackingId; ISyncSyncDigestInfoStore syncDigestStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetSyncDigestStore(sdataContext); SyncDigestInfo syncDigestInfo = syncDigestStore.Get(resourceKind); if (count == 0) count = 10; feed = new SyncFeed(); Token emptyToken = new Token(); feed.Digest = new SyncFeedDigest(); feed.Digest.Origin = _parentPerformer._requestContext.OriginEndPoint; feed.Digest.Entries = new List<SyncFeedDigestEntry>(); if (syncDigestInfo != null) { foreach (SyncDigestEntryInfo entryinfo in syncDigestInfo) { SyncFeedDigestEntry entry = new SyncFeedDigestEntry(); entry.ConflictPriority = entryinfo.ConflictPriority; entry.Endpoint = entryinfo.Endpoint; entry.Tick = entryinfo.Tick; entry.Stamp = DateTime.Now; feed.Digest.Entries.Add(entry); } } IEntityWrapper wrapper = EntityWrapperFactory.Create(resource, _parentPerformer._requestContext); for (int index = startIndex; index < ((startIndex + count > correlatedResSyncInfos.Count) ? correlatedResSyncInfos.Count : startIndex + count); index++) { CorrelatedResSyncInfo resSyncInfo = (CorrelatedResSyncInfo)correlatedResSyncInfos[index]; SyncFeedEntry entry = wrapper.GetFeedEntry(resSyncInfo); if (entry != null) feed.Entries.Add(entry); else { entry = new SyncFeedEntry(); entry.HttpMethod = "DELETE"; entry.Uuid = resSyncInfo.ResSyncInfo.Uuid; feed.Entries.Add(entry); } } // initialize the feed string url = string.Format("{0}/$syncSource('{1}')", endpoint, trackingId); feed.Id = url; feed.Title = resourceKind; #region PAGING & OPENSEARCH int totalResults = correlatedResSyncInfos.Count; PageController pageController = new PageController(startIndex+1, FeedMetadataHelpers.DEFAULT_ITEMS_PER_PAGE, totalResults, count, url); /* PAGING */ FeedLinkCollection feedLinks = new FeedLinkCollection(); feedLinks.Add(new FeedLink(pageController.GetLinkSelf(), LinkType.Self, MediaType.Atom, "Current Page")); feedLinks.Add(new FeedLink(pageController.GetLinkFirst(), LinkType.First, MediaType.Atom, "First Page")); feedLinks.Add(new FeedLink(pageController.GetLinkLast(), LinkType.Last, MediaType.Atom, "Last Page")); string linkUrl; if (pageController.GetLinkNext(out linkUrl)) feedLinks.Add(new FeedLink(linkUrl, LinkType.Next, MediaType.Atom, "Next Page")); if (pageController.GetLinkPrevious(out linkUrl)) feedLinks.Add(new FeedLink(linkUrl, LinkType.Previous, MediaType.Atom, "Previous Page")); feed.Links = feedLinks; /* OPENSEARCH */ feed.Opensearch_ItemsPerPageElement = pageController.GetOpensearch_ItemsPerPageElement(); feed.Opensearch_StartIndexElement = pageController.GetOpensearch_StartIndexElement(); feed.Opensearch_TotalResultsElement = pageController.GetOpensearch_TotalResultsElement(); #endregion //feed.Id = url; //if (startIndex + count < correlatedResSyncInfos.Count) //{ // FeedLink linkNext = new FeedLink(string.Format("{0}?startIndex={1}&count=10", url, startIndex + count), LinkType.Next); // feed.Links.Add(linkNext); //} //FeedLink linkFirst = new FeedLink(String.Format("{0}?startIndex=0&count=10", url), LinkType.First); //feed.Links.Add(linkFirst); //FeedLink linkSelf = new FeedLink(String.Format("{0}?startIndex={1}&count=10", url, startIndex), LinkType.Self); //feed.Links.Add(linkSelf); return feed; }
public override SyncFeedEntry GetFeedEntry(string id) { SyncFeedEntry result = new SyncFeedEntry(); result.Payload = PayloadFactory.CreatePayload(_resourceKind); Identity identity; AccountDocument accountDocument; Account account = new Account(); identity = new Identity(account.EntityName, id); accountDocument = (AccountDocument)_entity.GetDocument(identity, _emptyToken, _context.Config); if (accountDocument.LogState == LogState.Deleted) return null; if (accountDocument.addresses.documents.Count == 0) return null; Document document = accountDocument.addresses.documents[0]; List<SyncFeedEntryLink> links; result.Payload = GetTransformedPayload(document, out links); string taUuid = GetTradingAccountUuid(accountDocument.Id); if (!String.IsNullOrEmpty(taUuid)) { SyncFeedEntryLink tradingAccountLink = SyncFeedEntryLink.CreateRelatedLink( String.Format("{0}{1}('{2}')", _context.DatasetLink, SupportedResourceKinds.tradingAccounts.ToString(), accountDocument.Id), SupportedResourceKinds.tradingAccounts.ToString(), _tradingAccountUuidPayloadPath, taUuid); links.Add(tradingAccountLink); } result.SyncLinks = links; result.Id = String.Format("{0}{1}('{2}')", _context.DatasetLink, _resourceKind.ToString(), id); result.Title = String.Format("{0}: {1}", _resourceKind.ToString(), id); result.Updated = DateTime.Now; return result; }
public void DoWork(IRequest request) { EntryRequestType entryRequestType; string resourceKindName; ICorrelatedResSyncInfoStore correlatedResSyncStore; bool includeDataPayloads; //?includePayloads value CorrelatedResSyncInfo[] correlatedResSyncInfos; int totalResult; #region initialization // multi or single entry request? entryRequestType = (String.IsNullOrEmpty(_requestContext.ResourceKey)) ? EntryRequestType.Multi : EntryRequestType.Single; resourceKindName = _requestContext.ResourceKind.ToString(); string tmpValue; // ?includePayloads includeDataPayloads = false; // default value, but check for settings now if (_requestContext.SdataUri.QueryArgs.TryGetValue("includePayload", out tmpValue)) includeDataPayloads = System.Xml.XmlConvert.ToBoolean(tmpValue); // get store to request the correlations correlatedResSyncStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_requestContext.SdataContext); #endregion // receive correlated resource synchronization entries if (entryRequestType == EntryRequestType.Multi) { int pageNumber = FeedMetadataHelpers.GetPageNumber(_requestContext); correlatedResSyncInfos = correlatedResSyncStore.GetPaged(resourceKindName, pageNumber, FeedMetadataHelpers.DEFAULT_ITEMS_PER_PAGE, out totalResult); } else { Guid uuid = (Guid)TypeDescriptor.GetConverter(typeof(Guid)).ConvertFrom(_requestContext.ResourceKey); correlatedResSyncInfos = correlatedResSyncStore.GetByUuid(resourceKindName, new Guid[]{uuid}); totalResult = correlatedResSyncInfos.Length; } // Create the feed SyncFeed feed = new SyncFeed(); // initialize the feed feed.Id = FeedMetadataHelpers.BuildBaseUrl(_requestContext, FeedMetadataHelpers.RequestKeywordType.linked); feed.Title = string.Format("{0} Linking Feed", resourceKindName); #region PAGING & OPENSEARCH /* PAGING */ feed.Links = FeedMetadataHelpers.CreatePageFeedLinks(_requestContext, totalResult, FeedMetadataHelpers.RequestKeywordType.linked); /* OPENSEARCH */ PageController pageLinkBuilder = FeedMetadataHelpers.GetPageLinkBuilder(_requestContext, totalResult, FeedMetadataHelpers.RequestKeywordType.linked); feed.Opensearch_ItemsPerPageElement = pageLinkBuilder.GetOpensearch_ItemsPerPageElement(); feed.Opensearch_StartIndexElement = pageLinkBuilder.GetOpensearch_StartIndexElement(); feed.Opensearch_TotalResultsElement = pageLinkBuilder.GetOpensearch_TotalResultsElement(); #endregion feed.FeedType = (entryRequestType == EntryRequestType.Multi) ? FeedType.Linked : FeedType.LinkedSingle; IEntityWrapper wrapper = null; if (includeDataPayloads) wrapper = EntityWrapperFactory.Create(_requestContext.ResourceKind, _requestContext); for (int i=0; i<correlatedResSyncInfos.Length; i++) { SyncFeedEntry entry = null; if (includeDataPayloads) { entry = wrapper.GetFeedEntry(correlatedResSyncInfos[i].LocalId); } else { entry = new SyncFeedEntry(); } entry.Id = FeedMetadataHelpers.BuildLinkedEntryUrl(_requestContext, correlatedResSyncInfos[i].ResSyncInfo.Uuid); entry.Title = FeedMetadataHelpers.BuildLinkedEntryTitle(_requestContext, correlatedResSyncInfos[i].ResSyncInfo.Uuid); #region LINKED ELEMENT LinkedElement linkedElement = new LinkedElement(); linkedElement.Resource = FeedMetadataHelpers.BuildEntryResourceUrl(_requestContext, correlatedResSyncInfos[i].LocalId); linkedElement.Uuid = correlatedResSyncInfos[i].ResSyncInfo.Uuid; entry.Linked = linkedElement; #endregion feed.Entries.Add(entry); } request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = (IFeed)feed; request.Response.ContentType = (entryRequestType == EntryRequestType.Multi) ? MediaType.Atom : MediaType.AtomEntry; }
public void DoWork(IRequest request) { //// check for resource kind existence and supported types //if (_requestContext.ResourceKind == SupportedResourceKinds.None) // throw new RequestException("Invalid request: The service {0} is a resource based service."); // if request was done in the context of a resource kind -> check for supported resourceKinds if (_requestContext.ResourceKind != SupportedResourceKinds.None) { if (!(_requestContext.ResourceKind == SupportedResourceKinds.salesOrders || _requestContext.ResourceKind == SupportedResourceKinds.salesOrderLines)) throw new RequestException("Invalid request: The service {0} is only supported by salesOrders and salesOrderLines."); } // read entry from stream SyncFeedEntry entry = new SyncFeedEntry(); XmlReader reader = XmlReader.Create(request.Stream); reader.MoveToContent(); entry.ReadXml(reader, typeof(ComputePriceRequestPayload)); // underlying resources for price computing computePriceRequesttype computePriceRequest; CommodityIdentity[] productIds; // will contain local ids of the commodity referenced by line items. // Could contain empty items if commodity id could not be found. // compute price computePriceResponsetype computePriceResponse; if (null == entry.Payload) throw new RequestException("Invalid Xml input stream. Could not parse the payload."); computePriceRequest = ((ComputePriceRequestPayload)entry.Payload).ComputePriceRequest; #region Trading Account (discounted) // get the undelying trading account // is null if request is not in the context of an account. // if no account found means that the account exists in CRM only // and has not yet been linked with ERP. ERP could treat the account as a // prospect account and generate a price for the account, or if this is not // possible it could return an error. #endregion #region PricingDocument (only salesOrder suppported) // document associated with this pricing request (e.g. the Sales Order, Quotation, Purchase Order number). // could be of any type of enumeration "pricingDocumenttype" // if no document exists the request is not in the context of a document like order, quote, etc. // TODO: Get the associated document to find out the context of the request. // Validate document type // here: only salesOrder supported // removed to avoid possible errors (OK as this is only an example project) //if (computePriceRequest.pricingDocumentType != pricingDocumenttype.salesOrder) // throw new RequestException("Invalid document type in element 'pricingDocumentType' defined. Only salesOrder supported."); #endregion #region Commodities (for each document lines) // Get Commodity local ids used on the associated document lines. // (the array returned is always of the same size as the number of document lines in the request payload. // If a commodity local id could not be found in feed entry the array item will be null. productIds = this.GetCommodityIds(entry); // (never returns null, but could return an empty array) #endregion // compute price PricingServices pricingServices = new PricingServices(_requestContext.Config); if (_requestContext.ResourceKind == SupportedResourceKinds.salesOrders || _requestContext.ResourceKind == SupportedResourceKinds.None) computePriceResponse = pricingServices.ComputePrice(computePriceRequest, productIds, false); else computePriceResponse = pricingServices.ComputePrice(computePriceRequest, productIds, true); // create response feed this.BuildResponseFeed(ref request, computePriceResponse); //this.BuildResponseFeed(ref request, cpr); }
private CommodityIdentity[] GetCommodityIds(SyncFeedEntry requestEntry) { List<CommodityIdentity> identities = new List<CommodityIdentity>(); // To be able to receive commodity data we need the local commodity ids. // These ids can be requested in 2 ways: // 1a) Using reference link: If attribute 'href' is a valid url of this adapter and has a resource key we can use the given resourceKey. // 1b) Using reference link: Otherwise we use the attribute 'uuid' and use the correlation repository. // 2) Using the value contained in line property 'uuid' and using the correlation repository. // If 1) and 2) do not succeed we add an empty object. ComputePriceRequestPayload payload = (ComputePriceRequestPayload)requestEntry.Payload; int noOflines = payload.ComputePriceRequest.pricingDocumentLines.Length; for(int i=0; i< noOflines; i++) { string payloadPath = string.Format("computePrice/pricingDocumentLines[{0}]/commodity", i); SyncFeedEntryLink feedLink = Helper.FindLinkByPayloadPath(requestEntry.SyncLinks.ToArray(), payloadPath); //if (null == feedLink) // throw new RequestException(string.Format("Link for payloadPath '{0}' missing", payloadPath)); if (null != feedLink) { // validate string strRel = SyncFeedEntryLink.GetRelString(Sage.Integration.Northwind.Feeds.RelEnum.related); if (feedLink.LinkRel != strRel) throw new RequestException(string.Format("Parsing link with payloadPath '{0}' failed: Attribute 'rel' must contain value '{1}'.", payloadPath, strRel)); // TODO: excluded because condition linktype could have valid space character and we cannot check this in a simple way. //string strType = SyncFeedEntryLink.GetTypeString(Sage.Integration.Northwind.Feeds.LinkTypeEnum.entry); //if (feedLink.LinkType != strType) // throw new RequestException(string.Format("Parsing link with payloadPath '{0}' failed: Invalid media type defined in attribute 'type'. Value '{1}' expected.", payloadPath, strType)); string url = feedLink.Href; // 1a) Try to parse href if (url.StartsWith(_requestContext.DatasetLink + SupportedResourceKinds.commodities.ToString())) { RequestContext tmpRequestContext = new RequestContext(new SDataUri(url)); if (tmpRequestContext.RequestType == RequestType.Resource) { identities.Add(new CommodityIdentity(tmpRequestContext.ResourceKey)); continue; // continue iteration (parse next line item) } } // 1b) Try to get uuid from link and to get the local id using synch correlation (linking) if (!string.IsNullOrEmpty(feedLink.Uuid)) { Guid uuid = (Guid)TypeDescriptor.GetConverter(typeof(Guid)).ConvertFrom(feedLink.Uuid); ICorrelatedResSyncInfoStore correlationStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_requestContext.SdataContext); CorrelatedResSyncInfo[] correlations = correlationStore.GetByUuid(SupportedResourceKinds.commodities.ToString(), new Guid[] { uuid }); if (correlations.Length == 1) { identities.Add(new CommodityIdentity(correlations[0].LocalId)); continue; // continue iteration (parse next line item) } } } else { // 2) Try to get uuid from property named 'uuid' string strLineUuid = payload.ComputePriceRequest.pricingDocumentLines[i].uuid; if (!string.IsNullOrEmpty(strLineUuid)) { Guid uuid = (Guid)TypeDescriptor.GetConverter(typeof(Guid)).ConvertFrom(strLineUuid); // get the local id using synch correlation (linking) ICorrelatedResSyncInfoStore correlationStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_requestContext.SdataContext); CorrelatedResSyncInfo[] correlations = correlationStore.GetByUuid(SupportedResourceKinds.commodities.ToString(), new Guid[] { uuid }); if (correlations.Length == 1) { identities.Add(new CommodityIdentity(correlations[0].LocalId)); continue; // continue iteration (parse next line item) } } } // If 1) and 2) failed add an empty CommodityIdentity identities.Add(CommodityIdentity.Empty); } return identities.ToArray(); }
private void BuildResponseFeed(ref IRequest request, computePriceResponsetype computePriceResponse) { ComputePriceResponsePayload computePriceResponsePayload = new ComputePriceResponsePayload(); computePriceResponsePayload.ComputePriceResponse = computePriceResponse; SyncFeedEntry entry = new SyncFeedEntry(); entry.Payload = computePriceResponsePayload; entry.Id = _requestContext.SdataUri.Uri.ToString(); entry.Title = "computePrice"; entry.Updated = DateTime.Now; //entry.Links //entry.Categories SyncFeed feed = new SyncFeed(); feed.FeedType = FeedType.ResourceEntry; feed.Entries.Add(entry); request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = feed; request.Response.ContentType = Sage.Common.Syndication.MediaType.AtomEntry; }
public override SyncFeedEntry GetFeedEntry(string id) { SyncFeedEntry result = new SyncFeedEntry(); result.Payload = PayloadFactory.CreatePayload(_resourceKind); Identity identity; AccountDocument accountDocument; Account account = new Account(); string accountId; if (id.EndsWith(Sage.Integration.Northwind.Application.API.Constants.PhoneIdPostfix)) accountId = id.Replace(Sage.Integration.Northwind.Application.API.Constants.PhoneIdPostfix, ""); else if (id.EndsWith(Sage.Integration.Northwind.Application.API.Constants.FaxIdPostfix)) accountId = id.Replace(Sage.Integration.Northwind.Application.API.Constants.FaxIdPostfix, ""); else return null; identity = new Identity(account.EntityName, accountId); accountDocument = (AccountDocument)_entity.GetDocument(identity, _emptyToken, _context.Config); if (accountDocument.LogState == LogState.Deleted) return null; if (accountDocument.addresses.documents.Count == 0) return null; Document document = null; foreach (Document phoneDoc in accountDocument.phones.documents) { if (phoneDoc.Id.Equals(id, StringComparison.InvariantCultureIgnoreCase)) { document = phoneDoc; break; } } if (document == null) return null; List<SyncFeedEntryLink> links; result.Payload = GetTransformedPayload(document, out links); string taUuid = GetTradingAccountUuid(accountDocument.Id); if (!String.IsNullOrEmpty(taUuid)) { SyncFeedEntryLink tradingAccountLink = SyncFeedEntryLink.CreateRelatedLink( String.Format("{0}{1}('{2}')", _context.DatasetLink, SupportedResourceKinds.tradingAccounts.ToString(), accountDocument.Id), SupportedResourceKinds.tradingAccounts.ToString(), _tradingAccountUuidPayloadPath, taUuid); links.Add(tradingAccountLink); } result.SyncLinks = links; result.Id = String.Format("{0}{1}('{2}')", _context.DatasetLink, _resourceKind.ToString(), id); result.Title = String.Format("{0}: {1}", _resourceKind.ToString(), id); result.Updated = DateTime.Now; return result; }
public void DoWork(IRequest request) { // only atom entry supported!!! if (request.ContentType != Sage.Common.Syndication.MediaType.AtomEntry) throw new RequestException("Atom entry content type expected"); // deserialize the request stream to a SyncFeedEntry SyncFeedEntry entry = new SyncFeedEntry(); XmlReader reader = XmlReader.Create(request.Stream); reader.MoveToContent(); entry.ReadXml(reader, typeof(SyncDigestPayload)); if (null == entry.Linked) throw new RequestException("Invalid content: element 'linked' missing"); // We check for equality of the urls of the request url and // in the linked element up to the resourceKind. string requestEndpointUrl = _requestContext.OriginEndPoint; RequestContext entryResourceAsRequestContext = new RequestContext(new Sage.Common.Syndication.SDataUri(entry.Linked.Resource)); // TODO: not really nice here. string linkedResourceUrl = entryResourceAsRequestContext.OriginEndPoint; if (!string.Equals(requestEndpointUrl, linkedResourceUrl, StringComparison.InvariantCultureIgnoreCase)) throw new RequestException("Request url and linked entry resource not matching."); string resourceKindName = _requestContext.ResourceKind.ToString(); Guid currentUuid = (Guid)TypeDescriptor.GetConverter(typeof(Guid)).ConvertFrom(_requestContext.ResourceKey); Guid newUuid = entry.Linked.Uuid; string newLocalId = entryResourceAsRequestContext.SdataUri.CollectionPredicate; // update the correlation entry in the sync store. // if the uuid should be updated we have to remove the current correlation and then add a new one. // otherwise we update the current correlation. ICorrelatedResSyncInfoStore correlatedResSyncInfoStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_requestContext.SdataContext); // create the target correlation ResSyncInfo targetResSyncInfo = new ResSyncInfo(newUuid, requestEndpointUrl, 0, string.Empty, DateTime.Now); CorrelatedResSyncInfo targetInfo = new CorrelatedResSyncInfo(newLocalId, targetResSyncInfo); if (currentUuid == newUuid) { correlatedResSyncInfoStore.Update(resourceKindName, targetInfo); } else { correlatedResSyncInfoStore.Delete(resourceKindName, currentUuid); correlatedResSyncInfoStore.Add(resourceKindName, targetInfo); } // create response SyncFeed feed = new SyncFeed(); feed.FeedType = FeedType.LinkedSingle; // create entry SyncFeedEntry responseEntry = new SyncFeedEntry(); responseEntry.Id = FeedMetadataHelpers.BuildLinkedEntryUrl(_requestContext, targetInfo.ResSyncInfo.Uuid); responseEntry.Title = FeedMetadataHelpers.BuildLinkedEntryTitle(_requestContext, targetInfo.ResSyncInfo.Uuid); LinkedElement linkedElement = new LinkedElement(); linkedElement.Resource = FeedMetadataHelpers.BuildEntryResourceUrl(_requestContext, targetInfo.LocalId); linkedElement.Uuid = targetInfo.ResSyncInfo.Uuid; responseEntry.Linked = linkedElement; feed.Entries.Add(responseEntry); request.Response.Serializer = new SyncFeedSerializer(); request.Response.Feed = (IFeed)feed; request.Response.ContentType = MediaType.AtomEntry; }
public void ReadXml(System.Xml.XmlReader reader, Type payloadType) { reader.MoveToContent(); if ((reader.NodeType == System.Xml.XmlNodeType.Element) && (reader.LocalName == "feed") && (reader.NamespaceURI == Namespaces.atomNamespace)) { bool reading = true; while (reading) { if (reader.NodeType == System.Xml.XmlNodeType.Element) { switch(reader.LocalName) { case "title": reading = reader.Read(); if (reader.NodeType == System.Xml.XmlNodeType.Text) this.Title = reader.Value; break; case "id": reading = reader.Read(); if (reader.NodeType == System.Xml.XmlNodeType.Text) this.Id = reader.Value; break; case "entry": SyncFeedEntry entry = new SyncFeedEntry(); entry.ReadXml(reader, payloadType); this.Entries.Add(entry); break; case "digest": System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(SyncFeedDigest)); object obj = serializer.Deserialize(reader); if (obj is SyncFeedDigest) this.Digest = obj as SyncFeedDigest; break; case "itemsPerPage": reading = reader.Read(); if (reader.NodeType == System.Xml.XmlNodeType.Text) { IItemsPerPageElement itemsPerPageElement = FeedComponentFactory.Create<IItemsPerPageElement>(); itemsPerPageElement.LoadXmlValue(reader.Value); } break; case "startIndex": reading = reader.Read(); if (reader.NodeType == System.Xml.XmlNodeType.Text) { IStartIndexElement startIndexElement = FeedComponentFactory.Create<IStartIndexElement>(); startIndexElement.LoadXmlValue(reader.Value); } break; case "totalResults": reading = reader.Read(); if (reader.NodeType == System.Xml.XmlNodeType.Text) { ITotalResultsElement totalResultsElement = FeedComponentFactory.Create<ITotalResultsElement>(); totalResultsElement.LoadXmlValue(reader.Value); } break; default: reading = reader.Read(); break; } } else{ reading = reader.Read(); } } } }
public virtual SyncFeedEntry GetFeedEntry(SdataTransactionResult transactionResult) { SyncFeedEntry result; if (!String.IsNullOrEmpty(transactionResult.LocalId)) { result = GetFeedEntry(transactionResult.LocalId); } else { result = new SyncFeedEntry(); result.Uuid = transactionResult.Uuid; } if (result == null) return null; //entry.Uuid = transactionResult.Uuid; result.HttpStatusCode = transactionResult.HttpStatus; result.HttpMessage = transactionResult.HttpMessage; ; result.HttpMethod = transactionResult.HttpMethod; result.HttpLocation = transactionResult.Location; result.HttpETag = transactionResult.Etag; return result; }
public override SyncFeed GetFeed() { bool includeUuid; string whereClause = string.Empty; OleDbParameter[] oleDbParameters = null; if (this is IEntityQueryWrapper) { QueryFilterBuilder queryFilterBuilder = new QueryFilterBuilder((IEntityQueryWrapper)this); queryFilterBuilder.BuildSqlStatement(_context, out whereClause, out oleDbParameters); } SyncFeed feed = new SyncFeed(); feed.Title = _resourceKind.ToString() + ": " + DateTime.Now.ToString(); Token emptyToken = new Token(); List<Identity> identities = new List<Identity>(); if (String.IsNullOrEmpty(_context.ResourceKey)) identities = _entity.GetAll(_context.Config, whereClause, oleDbParameters); else identities.Add(new Identity(_entity.EntityName, _context.ResourceKey)); int totalResult = identities.Count; #region PAGING & OPENSEARCH /* PAGING */ feed.Links = FeedMetadataHelpers.CreatePageFeedLinks(_context, totalResult, FeedMetadataHelpers.RequestKeywordType.none); /* OPENSEARCH */ PageController pageController = FeedMetadataHelpers.GetPageLinkBuilder(_context, totalResult, FeedMetadataHelpers.RequestKeywordType.none); feed.Opensearch_ItemsPerPageElement = pageController.GetOpensearch_ItemsPerPageElement(); feed.Opensearch_StartIndexElement = pageController.GetOpensearch_StartIndexElement(); feed.Opensearch_TotalResultsElement = pageController.GetOpensearch_TotalResultsElement(); #endregion feed.Id = _context.SdataUri.ToString(); string tmpValue; // ?includeUuid includeUuid = false; // default value, but check for settings now if (_context.SdataUri.QueryArgs.TryGetValue("includeUuid", out tmpValue)) includeUuid = System.Xml.XmlConvert.ToBoolean(tmpValue); ICorrelatedResSyncInfoStore correlatedResSyncStore = null; if (includeUuid) // get store to request the correlations correlatedResSyncStore = RequestReceiver.NorthwindAdapter.StoreLocator.GetCorrelatedResSyncStore(_context.SdataContext); for (int pageIndex = pageController.StartIndex; pageIndex <= pageController.LastIndex; pageIndex++) //for (int index = startIndex; index < startIndex + count; index++) { int zeroBasedIndex = pageIndex - 1; Identity identity = identities[zeroBasedIndex]; AccountDocument accountDocument = (AccountDocument)_entity.GetDocument(identity, emptyToken, _context.Config); if (accountDocument.LogState == LogState.Deleted) continue; SyncFeedEntry entry = new SyncFeedEntry(); if (accountDocument.addresses.documents.Count == 0) return null; entry.Id = String.Format("{0}{1}('{2}')", _context.DatasetLink, _resourceKind.ToString(), identity.Id); entry.Title = String.Format("{0}: {1}", _resourceKind.ToString(), identity.Id); entry.Updated = DateTime.Now; if (_context.SdataUri.Precedence == null) { List<SyncFeedEntryLink> links; Document document = accountDocument.addresses.documents[0]; entry.Payload = GetTransformedPayload(document, out links); string taUuid = GetTradingAccountUuid(accountDocument.Id); if (!String.IsNullOrEmpty(taUuid)) { SyncFeedEntryLink tradingAccountLink = SyncFeedEntryLink.CreateRelatedLink( String.Format("{0}{1}('{2}')", _context.DatasetLink, SupportedResourceKinds.tradingAccounts.ToString(), accountDocument.Id), SupportedResourceKinds.tradingAccounts.ToString(), _tradingAccountUuidPayloadPath, taUuid); links.Add(tradingAccountLink); } entry.SyncLinks = links; } if (includeUuid) { CorrelatedResSyncInfo[] infos = correlatedResSyncStore.GetByLocalId(_context.ResourceKind.ToString(), new string[] { identity.Id }); entry.Uuid = (infos.Length > 0) ? infos[0].ResSyncInfo.Uuid : Guid.Empty; } if (entry != null) feed.Entries.Add(entry); } return feed; }
public virtual SyncFeedEntry GetFeedEntry(string id) { SyncFeedEntry result = new SyncFeedEntry(); result.Payload = PayloadFactory.CreatePayload(_resourceKind); Identity identity; Document document; identity = GetIdentity(id); document = _entity.GetDocument(identity, _emptyToken, _context.Config); if (document.LogState == LogState.Deleted) return null; List<SyncFeedEntryLink> links; result.Payload = GetTransformedPayload(document, out links); result.Id = String.Format("{0}{1}('{2}')", _context.DatasetLink, _resourceKind.ToString(), id); result.Title = String.Format("{0}: {1}", _resourceKind.ToString(), id); result.Updated = DateTime.Now; result.SyncLinks = links; return result; }
/// <summary> /// /// </summary> /// <param name="config"></param> /// <returns></returns> /// <remarks>This method is not threadsafe as the performer must be finished when calling this method.</remarks> public SyncFeed GetFeed(NorthwindConfig config, int startIndex, int count) { SyncFeed feed; SdataContext sdataContext; SupportedResourceKinds resource; string resourceKind; string endpoint; Guid trackingId; List<SdataTransactionResult> transactinResults; sdataContext = _parentPerformer._requestContext.SdataContext; resource = _parentPerformer._requestContext.ResourceKind; resourceKind = resource.ToString(); transactinResults = _parentPerformer._asyncStateObj.TransactionResults; endpoint = _parentPerformer._requestContext.DatasetLink + resourceKind; ; trackingId = _parentPerformer._requestContext.TrackingId; if (count == 0) count = 10; feed = new SyncFeed(); Token emptyToken = new Token(); IEntityWrapper wrapper = EntityWrapperFactory.Create(resource, _parentPerformer._requestContext); for (int index = startIndex; index < ((startIndex + count > transactinResults.Count) ? transactinResults.Count : startIndex + count); index++) { SdataTransactionResult transactionResult = (SdataTransactionResult)transactinResults[index]; SyncFeedEntry entry = wrapper.GetFeedEntry(transactionResult); if (entry != null) feed.Entries.Add(entry); else { entry = new SyncFeedEntry(); entry.Uuid = transactionResult.Uuid; entry.HttpStatusCode = transactionResult.HttpStatus; entry.HttpMessage = transactionResult.HttpMessage; ; entry.HttpMethod = transactionResult.HttpMethod; entry.HttpLocation = transactionResult.Location; entry.HttpETag = transactionResult.Etag; feed.Entries.Add(entry); } } // initialize the feed string url = string.Format("{0}/$syncTarget('{1}')", endpoint, trackingId); feed.Title = resourceKind; feed.Id = url; #region PAGING & OPENSEARCH int totalResults = transactinResults.Count; PageController pageController = new PageController(startIndex+1, FeedMetadataHelpers.DEFAULT_ITEMS_PER_PAGE, totalResults, count, url); /* PAGING */ FeedLinkCollection feedLinks = new FeedLinkCollection(); feedLinks.Add(new FeedLink(pageController.GetLinkSelf(), LinkType.Self, MediaType.Atom, "Current Page")); feedLinks.Add(new FeedLink(pageController.GetLinkFirst(), LinkType.First, MediaType.Atom, "First Page")); feedLinks.Add(new FeedLink(pageController.GetLinkLast(), LinkType.Last, MediaType.Atom, "Last Page")); string linkUrl; if (pageController.GetLinkNext(out linkUrl)) feedLinks.Add(new FeedLink(linkUrl, LinkType.Next, MediaType.Atom, "Next Page")); if (pageController.GetLinkPrevious(out linkUrl)) feedLinks.Add(new FeedLink(linkUrl, LinkType.Previous, MediaType.Atom, "Previous Page")); feed.Links = feedLinks; /* OPENSEARCH */ feed.Opensearch_ItemsPerPageElement = pageController.GetOpensearch_ItemsPerPageElement(); feed.Opensearch_StartIndexElement = pageController.GetOpensearch_StartIndexElement(); feed.Opensearch_TotalResultsElement = pageController.GetOpensearch_TotalResultsElement(); #endregion //if (startIndex + count < transactinResults.Count) //{ // FeedLink linkNext = new FeedLink(string.Format("{0}?startIndex={1}&count=10", url, startIndex + count), LinkType.Next); // feed.Links.Add(linkNext); //} //FeedLink linkFirst = new FeedLink(String.Format("{0}?startIndex=0&count=10", url), LinkType.First); //feed.Links.Add(linkFirst); //FeedLink linkSelf = new FeedLink(String.Format("{0}?startIndex={1}&count=10", url, startIndex), LinkType.Self); //feed.Links.Add(linkSelf); return feed; }
public void ReadXml(System.Xml.XmlReader reader, Type payloadType) { reader.MoveToContent(); if ((reader.NodeType == System.Xml.XmlNodeType.Element) && (reader.LocalName == "feed") && (reader.NamespaceURI == Namespaces.atomNamespace)) { bool reading = true; while (reading) { if (reader.NodeType == System.Xml.XmlNodeType.Element) { switch (reader.LocalName) { case "title": reading = reader.Read(); if (reader.NodeType == System.Xml.XmlNodeType.Text) { this.Title = reader.Value; } break; case "id": reading = reader.Read(); if (reader.NodeType == System.Xml.XmlNodeType.Text) { this.Id = reader.Value; } break; case "entry": SyncFeedEntry entry = new SyncFeedEntry(); entry.ReadXml(reader, payloadType); this.Entries.Add(entry); break; case "digest": System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(SyncFeedDigest)); object obj = serializer.Deserialize(reader); if (obj is SyncFeedDigest) { this.Digest = obj as SyncFeedDigest; } break; case "itemsPerPage": reading = reader.Read(); if (reader.NodeType == System.Xml.XmlNodeType.Text) { IItemsPerPageElement itemsPerPageElement = FeedComponentFactory.Create <IItemsPerPageElement>(); itemsPerPageElement.LoadXmlValue(reader.Value); } break; case "startIndex": reading = reader.Read(); if (reader.NodeType == System.Xml.XmlNodeType.Text) { IStartIndexElement startIndexElement = FeedComponentFactory.Create <IStartIndexElement>(); startIndexElement.LoadXmlValue(reader.Value); } break; case "totalResults": reading = reader.Read(); if (reader.NodeType == System.Xml.XmlNodeType.Text) { ITotalResultsElement totalResultsElement = FeedComponentFactory.Create <ITotalResultsElement>(); totalResultsElement.LoadXmlValue(reader.Value); } break; default: reading = reader.Read(); break; } } else { reading = reader.Read(); } } } }