public bool Run()
        {
            trace.Trace("Deserializing bulk sync data.");

            BulkSyncData syncData = config.BulkSyncData != null
                                        ? syncData = JsonConvert.DeserializeObject <BulkSyncData>(config.BulkSyncData)
                                        : new BulkSyncData();

            string primaryEmail = SharedLogic.GetPrimaryEmailField(config.SubscriberEmail);

            QueryExpression viewFilter = GetBulkSyncFilter(config, syncData, primaryEmail);

            var auth = Authenticator.GetAuthentication(config, orgService);
            var sub  = new Subscriber(auth, config.ListId);
            var mdh  = new MetadataHelper(orgService, trace);

            trace.Trace("Beginning the sync process.");

            do
            {
                viewFilter.PageInfo.PageNumber = syncData.PageNumber > 1
                                                    ? syncData.PageNumber
                                                    : 1;

                if (!string.IsNullOrWhiteSpace(syncData.PagingCookie))
                {
                    viewFilter.PageInfo.PagingCookie = syncData.PagingCookie;
                }

                trace.Trace("Processing page number {0}.", viewFilter.PageInfo.PagingCookie);

                // sync batch of 1000 contacts to CM list as subscribers
                EntityCollection contacts = orgService.RetrieveMultiple(viewFilter);

                syncData.PagingCookie = contacts.PagingCookie;
                syncData.PageNumber++;

                IEnumerable <Entity> invalidEmail = contacts.Entities.Where(e => !e.Attributes.Contains(primaryEmail) || string.IsNullOrWhiteSpace(e[primaryEmail].ToString()));
                syncData.NumberInvalidEmails += invalidEmail.Count();

                BulkImportResults importResults = null;

                var subscribers = GenerateSubscribersList(contacts.Entities.Except(invalidEmail), primaryEmail, mdh);
                if (!subscribers.Any())
                {
                    trace.Trace("No subscribers to import from contact page.");
                    continue;
                }

                trace.Trace("Starting subscriber import. {0} subscribers.", subscribers.Count());
                try
                {
                    importResults = sub.Import(subscribers,
                                               false,  // resubscribe
                                               false,  // queueSubscriptionBasedAutoResponders
                                               false); // restartSubscriptionBasedAutoResponders
                }
                catch (CreatesendException ex)
                {
                    trace.Trace("CreatesendException Error on subscriber import: " + ex.Error.Message);
                    trace.Trace("Exception Error on subscriber import: " + ex.Message);

                    syncData.BulkSyncErrors.Add(new BulkSyncError("import", ex.Error.Message, ""));
                    syncData.BulkSyncErrors.Add(new BulkSyncError("import", ex.Message, ""));

                    trace.Trace("CreatesendException error occurred: Subscriber import ended.");
                }

                trace.Trace("Subscriber import ended.");

                if (importResults.FailureDetails.Count > 0)
                {
                    if (syncData.BulkSyncErrors == null)
                    {
                        syncData.BulkSyncErrors = new List <BulkSyncError>();
                    }

                    // log the errors back into bulk sync data
                    foreach (var failure in importResults.FailureDetails)
                    {
                        syncData.BulkSyncErrors.Add(new BulkSyncError(failure.Code, failure.Message, failure.EmailAddress));
                    }
                }

                syncData.NumberSuccesses += importResults.TotalNewSubscribers;

                trace.Trace("Page: {0}", syncData.PageNumber);
                trace.Trace("More Records? {0}", contacts.MoreRecords);

                if (!contacts.MoreRecords)
                {
                    trace.Trace("No more records.");
                    trace.Trace("Sending email.");
                    try
                    {
                        var account      = new Account(auth);
                        var emailPayload = String.Format("{{ \"ToEmail\": \"{0}\", \"SubscriberCount\": \"{1}\", \"ListName\": \"{2}\" }}",
                                                         account.GetPrimaryContact(), syncData.NumberSuccesses, config.ListName);
                        SuccessEmailSender.SendEmail(config.AccessToken, emailPayload);
                    }
                    catch (Exception ex)
                    {
                        trace.Trace(ex.Message);
                        trace.Trace(ex.StackTrace);
                    }

                    trace.Trace("Clearing the sync data.");
                    syncData.PageNumber    = 1;
                    syncData.PagingCookie  = string.Empty;
                    syncData.UpdatedFields = null;

                    syncData.BulkSyncErrors.Clear();
                    syncData.NumberInvalidEmails = 0;
                    break;
                }
            }while (timer.ElapsedMilliseconds <= 90000);

            trace.Trace("Saving bulk data.");
            string bulkData = JsonConvert.SerializeObject(syncData);

            config.BulkSyncData       = bulkData;
            config.BulkSyncInProgress = syncData.PageNumber > 1;
            configService.SaveConfig(config);

            return(syncData.PageNumber <= 1); // if we're done return true
        }
        private QueryExpression GetBulkSyncFilter(CampaignMonitorConfiguration config, BulkSyncData syncData, string primaryEmail)
        {
            // retrieve contacts based on the filter, grabbing the columns specified either
            // in the fields to sync (on config entity) or fields specified in the bulkdata sync fields
            QueryExpression viewFilter;

            if (config.SyncViewId != null && config.SyncViewId != Guid.Empty)
            {
                viewFilter = SharedLogic.GetConfigFilterQuery(orgService, config.SyncViewId);
            }
            else
            {
                // if no view filter, sync all active contacts
                viewFilter = new QueryExpression("contact");
                viewFilter.Criteria.AddCondition(
                    new ConditionExpression("statecode", ConditionOperator.Equal, 0));
            }

            viewFilter.ColumnSet.Columns.Clear();
            foreach (var link in viewFilter.LinkEntities)
            {
                link.Columns.Columns.Clear();
            }

            if (syncData.UpdatedFields != null && syncData.UpdatedFields.Length > 0)
            {
                viewFilter.ColumnSet.Columns.AddRange(syncData.UpdatedFields);
            }
            else
            {
                viewFilter.ColumnSet.Columns.AddRange(config.SyncFields);
            }

            // add required fields for syncing if they are not a part of the filter
            if (!viewFilter.ColumnSet.Columns.Contains(primaryEmail))
            {
                viewFilter.ColumnSet.Columns.Add(primaryEmail);
            }

            if (!viewFilter.ColumnSet.Columns.Contains("fullname"))
            {
                viewFilter.ColumnSet.Columns.Add("fullname");
            }

            viewFilter.AddOrder("modifiedon", OrderType.Ascending);
            viewFilter.PageInfo.Count = BATCH_AMOUNT;
            viewFilter.PageInfo.ReturnTotalRecordCount = true;

            return(viewFilter);
        }