private void DoUpdateCategories(string key, DateTime since)
        {
            var count  = Channel.get_event_category_count(key, since);
            var offset = 0;

            if (count > 0)
            {
                _cacheCategories = null;
            }
            while (offset < count)
            {
                // get the next batch
                var a = Channel.get_event_categories(key, since, offset);
                offset += a.Length;
                foreach (var x in a)
                {
                    var id       = TicketJonesCategory.For(x.id);
                    var category = Categories.TryGet(id) ?? new TicketJonesCategory
                    {
                        Document = new Document {
                            Id = id
                        },
                    };
                    category.TicketJonesId = x.id.ToString();
                    category.Active        = x.active;
                    category.Description   = x.name;
                    Categories.Save(category);
                }
            }
        }
        public Event[] FindEventsNear(string zip)
        {
            CheckUpdate();
            var cx = GetCategories();
            var vn = GetVenues();
            var pf = GetPerformers();

            try
            {
                var a = Channel.get_events_by_zipcode(Application.TicketJonesKey, zip, DateTime.Now, 90);
                return(a.Select(x => new Event
                {
                    Id = TicketJonesEvent.For(x.id),
                    Title = x.name,
                    Date = x.date,
                    Category = cx[TicketJonesCategory.For(x.event_category_id)],
                    Venue = vn[TicketJonesVenue.For(x.venue_id)],
                    Performers = new [] { x.performer1, x.performer2 }.Where(y => y > 0).Select(y => pf[TicketJonesPerformer.For(y)]).ToList(),
                }).ToArray());
            }
            catch
            {
                return(new Event[0]);
            }
        }
        private void DoUpdatePeformers(string key, DateTime since)
        {
            var count  = Channel.get_performer_count(key, since);
            var offset = 0;

            if (count > 0)
            {
                _cachePerformers = null;
            }
            while (offset < count)
            {
                // get the next batch
                var a = Channel.get_performers(key, since, offset);
                offset += a.Length;
                foreach (var x in a)
                {
                    var id        = TicketJonesPerformer.For(x.id);
                    var performer = Performers.TryGet(id) ?? new TicketJonesPerformer
                    {
                        Document = new Document {
                            Id = id
                        },
                    };
                    performer.TicketJonesId = x.id.ToString();
                    performer.Name          = x.name;
                    performer.Category      = TicketJonesCategory.For(x.event_category_id);
                    Performers.Save(performer);
                }
            }
        }
        void DoUpdate(object ots)
        {
            var ts = (DateTime)ots;

            var k = Application.TicketJonesKey;

            // update lookup lists first
            lock (_sync)
            {
                Parallel.Invoke(
                    () => DoUpdateCategories(k, ts),
                    () => DoUpdateVenues(k, ts),
                    () => DoUpdatePeformers(k, ts)
                    );
            }

            // update events -- never get more than 3 months worth
            if (ts < DateTime.UtcNow.AddMonths(-3))
            {
                ts = DateTime.UtcNow.AddMonths(-3);
            }
            foreach (var a in GetEvents(k, ts))
            {
                lock (_sync)
                {
                    foreach (var x in a)
                    {
                        var id = TicketJonesEvent.For(x.id);
                        var e  = Events.TryGet(id) ?? new TicketJonesEvent
                        {
                            Document = new Document {
                                Id = id
                            },
                        };
                        e.TicketJonesId = x.id.ToString();
                        e.Date          = x.date;
                        e.Time          = x.time;
                        e.Title         = x.name;
                        e.Performers    = new List <string>();
                        if (x.performer1 > 1)
                        {
                            e.Performers.Add(TicketJonesPerformer.For(x.performer1));
                        }
                        if (x.performer2 > 1)
                        {
                            e.Performers.Add(TicketJonesPerformer.For(x.performer2));
                        }
                        e.Venue    = TicketJonesVenue.For(x.venue_id);
                        e.Category = TicketJonesCategory.For(x.event_category_id);
                        Events.Save(e);
                    }
                }
            }

            LastUpdated = DateTime.UtcNow;
            _updating   = false;
        }