protected void MarketDataAdapterReplyEvent(Reply reply)
        {
            // process asynchronous reply
            try
            {
                // check for errors first
                if (reply == null)
                {
                    throw new ApplicationException(
                              String.Format("MarketDataAdapter_ReplyEvent() received null!"));
                }
                if (reply.ReplyError != null)
                {
                    throw new ApplicationException(
                              $"MarketDataAdapter_ReplyEvent() failed: {reply.ReplyError.DisplayName}: {reply.ReplyError.Description}");
                }
                string debugMsg = String.Format("[Received]");
                try
                {
                    debugMsg = debugMsg + $"[ReplyType={reply.ReplyType}]";
                    if (reply.Request != null)
                    {
                        debugMsg = debugMsg + $"[ReqId={reply.Request.RequestId}]";
                        SecuritiesDataCollection securityColl = reply.GetSecurityDataItems();
                        foreach (SecurityDataItem security in securityColl)
                        {
                            debugMsg = debugMsg + $"[{security.Security.Name}]";
                            debugMsg = security.FieldsData.Cast <FieldDataItem>().Aggregate(debugMsg, (current, dataNodeList) => current +
                                                                                            $"[{dataNodeList.Field.Mnemonic}]");
                        }

                        // map Bloomberg requestid to our subsId
                        RealtimeRequestMap subsMap;
                        lock (SubsMapExternal)
                        {
                            SubsMapExternal.TryGetValue(reply.Request.RequestId, out subsMap);
                        }
                        if (subsMap != null)
                        {
                            if (DateTimeOffset.Now > subsMap.SubsExpires)
                            {
                                MarketDataAdapter.CancelRequest(reply.Request.RequestId);
                                Logger.LogDebug("[Cancelled][ReqId={1}][SubsId={0}]", subsMap.InternalRequestId, reply.Request.RequestId);
                            }
                            Dictionary <string, BasicQuotation> providerResults = BuildProviderResultsIndex(reply);
                            ConsumerCallback(subsMap.InternalRequestId, new QuotedAssetSet {
                                assetQuote = Enumerable.ToArray(ConvertProviderResultsToStandardValuations(providerResults, subsMap.RequestContext))
                            });
                        }
                    }
                    else
                    {
                        debugMsg = debugMsg + "[RequestIdUnknown]";
                    }
                }
                catch (Exception e)
                {
                    debugMsg = debugMsg + Environment.NewLine + "!!!EXCEPTION!!! " + e.Message;
                }
                finally
                {
                    Logger.LogDebug(debugMsg);
                }
            }
            catch (Exception ex)
            {
                Logger.Log(ex);
                // don't rethrow
            }
        }
        private MDSResult <QuotedAssetSet> RunBloombergRequest(
            IModuleInfo clientInfo,
            Guid requestId,
            NamedValueSet requestParams,
            MDSRequestType requestType,
            DateTimeOffset subsExpires,
            QuotedAssetSet standardQuotedAssetSet)
        {
            // process request parameters
            //BloombergRequestProps requestProps = ProcessRequestParams(requestParams);

            // process the asset/quote lists to produce 1 or more instrument/field matrices
            RequestContext requestContext = ConvertStandardAssetQuotesToProviderInstrFieldCodes(requestType, standardQuotedAssetSet);

            // process the instr/field code sets
            var results = new List <BasicAssetValuation>();

            foreach (ProviderInstrFieldCodeSet instrFieldCodeSet in requestContext.ProviderInstrFieldCodeSets)
            {
                Request req;
                switch (requestType)
                {
                case MDSRequestType.Current:
                    req = new RequestForStatic {
                        SubscriptionMode = SubscriptionMode.ByRequest
                    };
                    break;

                case MDSRequestType.History:
                    req = new RequestForHistory {
                        SubscriptionMode = SubscriptionMode.ByRequest
                    };
                    throw new NotImplementedException("dataRequestType=History");

                case MDSRequestType.Realtime:
                    req = new RequestForRealtime {
                        SubscriptionMode = SubscriptionMode.ByField
                    };
                    break;

                default:
                    throw new NotSupportedException("dataRequestType");
                }

                // process request parameters
                //BloombergRequestProps requestProps = ProcessRequestParams(requestParams);

                // Multi-user mode logon check required. Note: some clients have exemptions:
                // - unit test clients
                // - server mode apps (eg. CurveGenerator)
                // Exempted clients must never forward any market data, only derived data.

                Logger.LogDebug("  Identity   : {0} ({1})", clientInfo.Name, clientInfo.UserFullName);
                Logger.LogDebug("  Application: {0} V{1}/{2} ({3}/{4})", clientInfo.ApplName, clientInfo.ApplNVer, clientInfo.ApplFVer, clientInfo.ApplPTok, clientInfo.ApplHash);
                Logger.LogDebug("  Core Client: {0} V{1}/{2} ({3}/{4})", clientInfo.CoreName, clientInfo.CoreNVer, clientInfo.CoreFVer, clientInfo.CorePTok, clientInfo.CoreHash);
                Logger.LogDebug("  Client Env.: {0} ({1} build)", clientInfo.ConfigEnv, clientInfo.BuildEnv);
                Logger.LogDebug("  Addresses  : {0} ({1},{2})", clientInfo.HostName, clientInfo.HostIpV4, String.Join(",", clientInfo.NetAddrs));

                // exemption list - todo - move to database
                //var exemptions = new List<ExemptClient>
                //                     {
                //                         new ExemptClient() {ConfigEnv = EnvId.DEV_Development, AssmName = "TestMds"},
                //                         new ExemptClient() {ConfigEnv = EnvId.DEV_Development, AssmName = "TestWebMdc"},
                //                         new ExemptClient() {AssmName = "nab.QDS.Workflow.CurveGeneration"}
                //                     };
                // - dev apps
                // - all environments

                //bool serverMode = IsExempt(clientInfo, exemptions.ToArray());
                var sapiLicense = new ServerApiLicense();
                if (true) // (serverMode)
                {
                    Logger.LogDebug("Server-mode: client logon check skipped.");
                    sapiLicense.CustomerAlias       = "";
                    sapiLicense.UUID                = 0;
                    sapiLicense.SID                 = 0;
                    sapiLicense.SIDInstance         = 0;
                    sapiLicense.TerminalSID         = 0;
                    sapiLicense.TerminalSIDInstance = 0;
                }
                //else
                //{
                //    _Logger.LogDebug("Multi-user-mode: logon check required.");
                //    sapiLicense.CustomerAlias = requestParams.GetValue<string>(MdpConfigName.Bloomberg_CustName, "unknown");
                //    sapiLicense.UUID = requestParams.GetValue<int>(MdpConfigName.Bloomberg_UUID, 0);
                //    sapiLicense.SID = requestParams.GetValue<int>(MdpConfigName.Bloomberg_SID, 0);
                //    sapiLicense.SIDInstance = requestParams.GetValue<int>(MdpConfigName.Bloomberg_SidN, 0);
                //    sapiLicense.TerminalSID = requestParams.GetValue<int>(MdpConfigName.Bloomberg_TSID, 0);
                //    sapiLicense.TerminalSIDInstance = requestParams.GetValue<int>(MdpConfigName.Bloomberg_TSidN, 0);
                //    IPAddress ipAddress = IPAddress.Parse(clientInfo.HostIpV4);
                //    TerminalMonitor.LogonStatus logonStatus
                //        = TerminalMonitor.Instance.GetLogonStatus(sapiLicense, ipAddress, 5000);
                //    if (logonStatus != TerminalMonitor.LogonStatus.LoggedOn)
                //        throw new ApplicationException(String.Format(
                //            "Bloomberg user (uuid={0}) is not logged on at terminal ({1})", sapiLicense.UUID, ipAddress));
                //}
                req.License = sapiLicense;

                // load instrument ids
                foreach (string instrId in instrFieldCodeSet.InstrumentIds)
                {
                    req.Securities.Add(instrId);
                }

                // load field names
                foreach (string fieldName in instrFieldCodeSet.FieldNames)
                {
                    req.Fields.Add(MarketDataAdapter.FieldTable[fieldName]);
                }

                // call Bloomberg
                if (requestType == MDSRequestType.Realtime)
                {
                    // realtime requests
                    MarketDataAdapter.SendRequest(req);
                    // save subsmap
                    lock (SubsMapExternal)
                    {
                        var subsMap = new RealtimeRequestMap(
                            requestId, req.RequestId.ToString(), requestParams, subsExpires, requestContext);
                        SubsMapExternal.Add(req.RequestId, subsMap);
                    }
                    Logger.LogDebug("[Sent][ReqId={0}][SubsId={1}]", req.RequestId, requestId);
                    return(null);
                }
                // snapshot (non-realtime) requests
                Reply reply = MarketDataAdapter.SynchronousRequest(req, 30000);
                // check for errors
                if (reply == null)
                {
                    throw new ApplicationException(
                              String.Format("SynchronousRequest() returned null!"));
                }
                if (reply.ReplyError != null)
                {
                    throw new ApplicationException(
                              $"SynchronousRequest() failed: {reply.ReplyError.DisplayName}: {reply.ReplyError.Description}");
                }
                // process provider results
                Dictionary <string, BasicQuotation> providerResults = BuildProviderResultsIndex(reply);
                results.AddRange(ConvertProviderResultsToStandardValuations(providerResults, requestContext));
            } // foreach request page
            return(new MDSResult <QuotedAssetSet>
            {
                Result = new QuotedAssetSet {
                    assetQuote = results.ToArray()
                }
            });
        }