/// <summary> /// Creates a TvBroadcastListing /// </summary> /// <param name="importTag"></param> /// <param name="channelSource"></param> /// <param name="info"></param> /// <returns></returns> public TvProgramBroadCastListing AddBroadcastListing(TvProgramBroadCastListingImport importTag, TvChannelListingSource channelSource, IAddTvBroadCastListingInfo info) { // check if this import's going to violate the PK by trying to import duplicate Listings (on the same ImportId) if (CheckForViolatedPK(importTag, channelSource, info) != null) throw new TvBroadCastListingAlreadyExistsException( string.Format( "An item with that PK (import, channel, startat) {{{0}, {1}, {2}}} already exists", importTag.TvProgramBroadCastListingImportId, channelSource.TvChannelId, info.StartAt ) ); // clean up any whitespace on the text fields and replace as nulls where possible info.ProgramTitle = info.ProgramTitle.Trim(); info.EpisodeTitle = string.IsNullOrWhiteSpace(info.EpisodeTitle) ? null : info.EpisodeTitle.Trim(); info.EpisodeDescription = string.IsNullOrWhiteSpace(info.EpisodeDescription) ? null : info.EpisodeDescription.Trim(); // try and find or generate program information from this listing TvProgram program = GetOrCreateTvProgramFromListingInfo(info); // try and find or generate episode information from this listing TvProgramEpisode episode = GetOrCreateTvProgramEpisodeFromListingInfo(program, info); // if we don't have an EndDate we can't create a listing if (info.EndAt == null || info.StartAt == info.EndAt) return null; // see if there's an existing listing for this channel with these exact times var existing = FindExistingListing(channelSource, info.StartAt, info.EndAt.Value); if (existing != null) { // check if it's the same in every way except the importer if( // TvChannelId, StartAt, EndAt, Superceded==false are done by the DB existing.ProgramTitle == info.ProgramTitle && existing.EpisodeTitle == info.EpisodeTitle && existing.EpisodeDescription == info.EpisodeDescription && existing.TvProgramId == program.TvProgramId && existing.TvProgramEpisodeId == episode?.TvProgramEpisodeId && existing.Attributes == info.Attributes ) { // already an exact match, return that instead return existing; } } // create and add the new listing var newItem = new TvProgramBroadCastListing { TvProgramBroadCastListingImportId = importTag.TvProgramBroadCastListingImportId, TvChannelId = channelSource.TvChannelId, StartAt = info.StartAt, EndAt = info.EndAt, ProgramTitle = info.ProgramTitle, EpisodeTitle = info.EpisodeTitle, EpisodeDescription = info.EpisodeDescription, TvProgramId = program?.TvProgramId, TvProgramEpisodeId = episode?.TvProgramEpisodeId, Attributes = info.Attributes, Superceded = false }; TvListings.Add(newItem); // if we had an existing item that wasn't an exact match we mark it as superceded since we've just added a new one if (existing != null) { existing.Superceded = true; TvListings.Update(existing); } // return the new item we just made return newItem; }
/// <summary> /// Checks for an exact PK match on local changes only /// </summary> /// <param name="importTag"></param> /// <param name="channelSource"></param> /// <param name="info"></param> /// <returns></returns> protected TvProgramBroadCastListing CheckForViolatedPK(TvProgramBroadCastListingImport importTag, TvChannelListingSource channelSource, IAddTvBroadCastListingInfo info) { // in-memory dupe detection to stop duplicate PKs generated by broken feed / import processes var local = ChangeTracker.Entries<TvProgramBroadCastListing>() .SingleOrDefault(x => x.Property(y => y.TvProgramBroadCastListingImportId).CurrentValue == importTag.TvProgramBroadCastListingImportId && x.Property(y => y.TvChannelId).CurrentValue == channelSource.TvChannelId && x.Property(y => y.StartAt).CurrentValue == info.StartAt ); if (local != null) return local.Entity; // we don't need to check the database here because this TvProgramBroadCastListingImportId has been created by this thread return null; }
/// <summary> /// Locates an existing listing for this channel at this time that hasn't been superceded /// </summary> /// <param name="channelSource"></param> /// <param name="info"></param> /// <returns></returns> protected TvProgramBroadCastListing FindExistingListing(TvChannelListingSource channelSource, DateTime startAt, DateTime endAt) { // check the local changetracker first var local = ChangeTracker.Entries<TvProgramBroadCastListing>() .SingleOrDefault(x => x.Property(y => y.StartAt).CurrentValue == startAt && x.Property(y => y.EndAt).CurrentValue == endAt && x.Property(y => y.TvChannelId).CurrentValue == channelSource.TvChannelId && x.Property(y => y.Superceded).CurrentValue == false ); // if we already have a matching PK for this listing just return it and don't add anything if (local != null) { return local.Entity; } // then the actual database var remote = TvListings .SingleOrDefault(x => x.StartAt == startAt && x.EndAt == endAt && x.TvChannelId == channelSource.TvChannelId && x.Superceded == false ); if (remote != null) { return remote; } return null; }
/// <summary> /// Locates or creates a TvChannel and its corresponding TvChannelListingSource /// </summary> /// <param name="import"></param> /// <param name="info"></param> /// <returns></returns> public TvChannelListingSource AddTvChannelListingSource(TvProgramBroadCastListingImport import, IAddTvChannelSourceInfo info) { // try and find a matching channel by uri var local = ChangeTracker.Entries<TvChannelListingSource>().SingleOrDefault(x => x.Property(y => y.SourceChannelUri).CurrentValue == info.Uri); if (local != null) return local.Entity; // try and find a matching channel by uri var remote = TvChannelListingSources.SingleOrDefault(x => x.SourceChannelUri == info.Uri); if (remote != null) return remote; // add a channel with that displayname var newChan = new TvChannel { ChannelName = info.DisplayName.Trim() }; TvChannels.Add(newChan); // and a link between it and the source with the info it needs for future requests var newSource = new TvChannelListingSource { SourceChannelUri = info.Uri, SourceChannelName = info.SourceName }; TvChannelListingSources.Add(newSource); return newSource; }