using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.ServiceModel; using System.ServiceModel.Activation; using System.ServiceModel.Channels; using System.ServiceModel.Web; using System.Text; using System.Threading.Tasks; using System.Web; using MoreLinq; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Sony.Filtr.API.Service; using Sony.Filtr.API.ViewModels.v3; using Sony.Filtr.API.WCF; using Sony.Filtr.Contracts.Abstractions; using Sony.Filtr.Contracts.Entities; using Sony.Filtr.PlaylistGeneration; namespace Sony.Filtr.API { [ServiceContract(Namespace = "JsonpAjaxService")] [ErrorHandler(typeof(ErrorHandler))] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] public class FiltrApi3 { public FiltrApi3(IApplicationInstanceManager applicationInstanceManager, SpotifyService3 spotifyService, DeezerService3 deezerService, BaseService3 baseService) { _spotifyService = spotifyService; _deezerService = deezerService; _commonService = baseService; _applicationInstanceManager = applicationInstanceManager; } private readonly SpotifyService3 _spotifyService; private readonly DeezerService3 _deezerService; private readonly BaseService3 _commonService; private readonly IApplicationInstanceManager _applicationInstanceManager; private Application GetMarketApplication(string market) { if (!string.IsNullOrWhiteSpace(market)) { int applicationId; if (int.TryParse(market, out applicationId)) { var application = _applicationInstanceManager.GetApplication(applicationId); if (application != null) return application; } } return _applicationInstanceManager.GetFallbackApplication(); } private IService3 GetService(string service) { if (service != null && service.Equals("spotify", StringComparison.InvariantCultureIgnoreCase)) return _spotifyService; if (service != null && service.Equals("deezer", StringComparison.InvariantCultureIgnoreCase)) return _deezerService; throw new WebFaultException(HttpStatusCode.BadRequest); } [OperationContract] [AspNetCacheProfile("CacheVeryLongTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/{countryCode}/tracks?tag={tag}&artist={artist}")] public async Task> GetTracks(string service, string countryCode, string market, string tag, string artist) { var application = GetMarketApplication(market); var selectedService = GetService(service); return await selectedService.GetTracks(application, countryCode, tag, artist); } [OperationContract] [AspNetCacheProfile("CacheLongTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/tags?artist={artists}&tag={tags}")] public async Task> GetTags(string service, string[] artists, string[] tags, string market) { var application = GetMarketApplication(market); var selectedService = GetService(service); return await selectedService.GetTagsAsync(application, artists, tags); } [OperationContract] [AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/season")] public SeasonViewModel GetSeason(string service, string market) { var application = GetMarketApplication(market); var selectedService = GetService(service); return selectedService.GetSeason(application); } [OperationContract] [AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{market}/autocomplete/{*searchString}")] public async Task> SearchAutocomplete(string searchString, string market) { var application = GetMarketApplication(market); var selectedService = _commonService; return await selectedService.SearchAutocompleteAsync(application, searchString); } [OperationContract] [AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/autocomplete/{*searchString}")] public async Task> SearchServiceAutocomplete(string service, string searchString, string market) { var application = GetMarketApplication(market); var selectedService = GetService(service); return await selectedService.SearchAutocompleteAsync(application, searchString); } [OperationContract] //[AspNetCacheProfile("CacheVeryShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/search/{*searchString}")] public async Task SearchEverything(string service, string market, string searchString) { var selectedService = GetService(service); var application = GetMarketApplication(market); var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; var searchResult = await selectedService.SearchAsync(application, searchString); return GetAsJsonMessage(searchResult, webOperationContext); } [OperationContract] //[AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/releases")] public async Task> GetNewReleases(string service, string market) { var application = GetMarketApplication(market); var selectedService = GetService(service); var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; var lastModifiedDate = await selectedService.GetNewReleasesLastModifiedDateAsync(application); if (webOperationContext != null) { CheckConditionalRetrieve(webOperationContext, lastModifiedDate); SetCachability(httpContext); webOperationContext.OutgoingResponse.LastModified = lastModifiedDate; } return await selectedService.GetNewReleasesAsync(application, null); //No countryCode filtering } [OperationContract] //[AspNetCacheProfile("CacheVeryShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/playlists?detailed={detailed}&playlistId={playlistId}&limit={limit}&offset={offset}")] public async Task> GetPlaylists(string service, string market, bool? detailed, string[] playlistId, int? limit, int? offset) { detailed = detailed ?? false; var application = GetMarketApplication(market); var selectedService = GetService(service); var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; var lastModifiedDate = await selectedService.GetPlaylistLastModifiedDateAsync(application); if (webOperationContext != null) { CheckConditionalRetrieve(webOperationContext, lastModifiedDate); SetCachability(httpContext); webOperationContext.OutgoingResponse.LastModified = lastModifiedDate; } var playlists = await selectedService.GetPlaylistsAsync(application, lastModifiedDate); playlists = playlists.OrderByDescending(p => p.Followers).ToList(); if (playlistId != null && playlistId.Any()) { var playlistIds = playlistId.Select(t => int.Parse(t)).ToHashSet(); playlists = playlists.Where(p => playlistIds.Contains(p.Id)).ToList(); } if (offset.HasValue) playlists = playlists.Skip(offset.Value).ToList(); if (limit.HasValue) playlists = playlists.Take(limit.Value).ToList(); var playlistList = playlists.Select(p=> FilterViewModel(p, detailed)).ToList(); return playlistList; } [OperationContract] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/categories")] public async Task> GetCategories(string service, string market) { var application = GetMarketApplication(market); var selectedService = GetService(service); var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; var lastModifiedDate = await _commonService.GetPlaylistCategoriesLastModifiedDateAsync(application); if (webOperationContext != null) { CheckConditionalRetrieve(webOperationContext, lastModifiedDate); SetCachability(httpContext); webOperationContext.OutgoingResponse.LastModified = lastModifiedDate; } return selectedService.GetCategories(application); } private static void SetCachability(HttpContext httpContext) { if (httpContext != null) { httpContext.Response.Cache.SetCacheability(HttpCacheability.Public); httpContext.Response.Cache.SetMaxAge(TimeSpan.Zero); } } [OperationContract] //[AspNetCacheProfile("CacheLongTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/playlists/{playlistId}?detailed={detailed}")] public async Task GetPlaylist(string service, string playlistId, bool? detailed) { var selectedService = GetService(service); var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; detailed = detailed ?? true; int id; if (!int.TryParse(playlistId, out id)) throw new WebFaultException(HttpStatusCode.NotFound); var playlist = await selectedService.GetEditorialPlaylistAsync(id); if (playlist == null) throw new WebFaultException(HttpStatusCode.NotFound); var application = GetMarketApplication(playlist.ApplicationID.ToString()); var lastModified = await selectedService.GetPlaylistLastModifiedDateAsync(application); if (webOperationContext != null) { CheckConditionalRetrieve(webOperationContext, lastModified); SetCachability(httpContext); webOperationContext.OutgoingResponse.LastModified = lastModified; } var playlistViewModels = await selectedService.GetPlaylistsAsync(application, lastModified); var viewModel = playlistViewModels.FirstOrDefault(p => p.Id == id); if (viewModel == null) throw new WebFaultException(HttpStatusCode.NotFound); return FilterViewModel(viewModel, detailed); } private PlaylistViewModel FilterViewModel(PlaylistViewModel viewModel, bool? detailed) { if (detailed.HasValue && detailed.Value == false) { viewModel.Description = null; } return viewModel; } [OperationContract] [AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/playlists/{playlistId}/tracks")] public async Task> GetPlaylistTracks(string service, string playlistId) { var selectedService = GetService(service); var playlistTracks = await selectedService.GetPlaylistTracksAsync(playlistId); if (playlistTracks == null) throw new WebFaultException(HttpStatusCode.NotFound); return playlistTracks; } [OperationContract] [AspNetCacheProfile("CacheLongTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/playlists/{playlistUri}")] public async Task GetPlaylistByUri(string service, string market, string playlistUri) { var selectedService = GetService(service); var application = GetMarketApplication(market); var playlists = await selectedService.GetPlaylistsAsync(application); var playlist = playlists.FirstOrDefault(p => p.Uri == playlistUri); if (playlist == null) throw new WebFaultException(HttpStatusCode.NotFound); return playlist; } [OperationContract] [AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/promoted")] public async Task> GetPromotedTracks(string service, string market) { var selectedService = GetService(service); var application = GetMarketApplication(market); return await selectedService.GetPromotedTracksAsync(application); } [OperationContract] [AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/popularArtists")] public async Task> GetPopularArtists(string service, string market) { var selectedService = GetService(service); var application = GetMarketApplication(market); return await _commonService.GetPopularArtistsAsync(application); } [OperationContract] [AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/globalization/{language}")] public Dictionary GetGlobalizationForLanguage(string service, string language) { var selectedService = GetService(service); return selectedService.GetGlobalizationForLanguage(language); } [OperationContract] //[AspNetCacheProfile("CacheLongTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{countryCode}/bootstrap")] public BootstrapViewModel GetBootstrap(string service, string countryCode) { var selectedService = GetService(service); return selectedService.GetBootstrapNew(countryCode); } [OperationContract] //[AspNetCacheProfile("CacheVeryShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "/{countryCode}/bootstrap")] public async Task GetGlobalBootstrap(string countryCode) { var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; var lastModified = await _commonService.GetGlobalBootstrapLastModifiedAsync(countryCode); if (webOperationContext != null) { CheckConditionalRetrieve(webOperationContext, lastModified); SetCachability(httpContext); webOperationContext.OutgoingResponse.LastModified = lastModified; } var bootstrapViewModel = _commonService.GetGlobalBootstrap(countryCode); return bootstrapViewModel; } [OperationContract] [AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/{countryCode}/searchTracks?artist={artist}&tag={tag}&minTempo={minTempo}&maxTempo={maxTempo}&minDanceability={minDanceability}&maxDanceability={maxDanceability}&minEnergy={minEnergy}&maxEnergy={maxEnergy}&minHotness={minHotness}&maxHotness={maxHotness}&minDuration={minDuration}&maxDuration={maxDuration}")] public Task> SearchTracksLocal(string service, string market, string countryCode, string[] artist, string tag, int? minTempo = null, int? maxTempo = null, double? minDanceability = null, double? maxDanceability = null, double? minEnergy = null, double? maxEnergy = null, double? minHotness = null, double? maxHotness = null, int? minDuration = null, int? maxDuration = null) { var selectedService = GetService(service); var application = GetMarketApplication(market); var searchTrackParameters = new SearchTracksParameters() { Tag = tag, Artists = artist, MinTempo = minTempo, MaxTempo = maxTempo, MinDanceability = HandleChangedFormat(minDanceability), MaxDanceability = HandleChangedFormat(maxDanceability), MinEnergy = HandleChangedFormat(minEnergy), MaxEnergy = HandleChangedFormat(maxEnergy), MinHotness = HandleChangedFormat(minHotness), MaxHotness = HandleChangedFormat(maxHotness), MinDuration = minDuration, MaxDuration = maxDuration, }; return selectedService.SearchTracksAsync(application, countryCode, searchTrackParameters); } [OperationContract] //[AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "/marketglobalization/{market}")] public async Task GetGlobalizationForMarket(string market) { var selectedService = _commonService; var application = GetMarketApplication(market); var webOperationContext = WebOperationContext.Current; var lastModified = await selectedService.GetMarketGlobalizationLastModifiedAsync(application); if (webOperationContext != null) { CheckConditionalRetrieve(webOperationContext, lastModified); webOperationContext.OutgoingResponse.LastModified = lastModified; } var marketGlobalizations = selectedService.GetMarketGlobalizations(application, context: null); return GetAsJsonMessage(marketGlobalizations, webOperationContext); } [OperationContract] //[AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "/marketglobalization/{market}/{context}")] public async Task GetGlobalizationForMarketContext(string market, string context) { var selectedService = _commonService; var application = GetMarketApplication(market); var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; var lastModified = await selectedService.GetMarketGlobalizationLastModifiedAsync(application); if (webOperationContext != null) { CheckConditionalRetrieve(webOperationContext, lastModified); SetCachability(httpContext); webOperationContext.OutgoingResponse.LastModified = lastModified; } var marketGlobalization = selectedService.GetMarketGlobalizations(application, context); return GetAsJsonMessage(marketGlobalization, webOperationContext); } [OperationContract] //[AspNetCacheProfile("CacheVeryShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/carouselImages")] public async Task GetCarouselImages(string service, string market) { var selectedService = GetService(service); var application = GetMarketApplication(market); var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; var lastModified = await _commonService.GetCarouselImageLastModifiedAsync(application); if (webOperationContext != null) { CheckConditionalRetrieve(webOperationContext, lastModified); SetCachability(httpContext); webOperationContext.OutgoingResponse.LastModified = lastModified; } var images = selectedService.GetCarouselImages(application); return GetAsJsonMessage(images, webOperationContext); } [OperationContract] //[AspNetCacheProfile("CacheVeryShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/filtrUsers")] public async Task GetFiltrUsers(string service) { var selectedService = GetService(service); var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; var lastModified = await selectedService.GetFiltrUsersLastModifiedAsync(); if (webOperationContext != null) { CheckConditionalRetrieve(webOperationContext, lastModified); SetCachability(httpContext); webOperationContext.OutgoingResponse.LastModified = lastModified; } var users = await selectedService.GetFiltrUsersAsync(); return GetAsJsonMessage(users, webOperationContext); } ////This is the old version which will be replaced with the above when testing has completed. //[OperationContract] ////[AspNetCacheProfile("CacheVeryShortTime")] //[WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/carouselImages")] //public async Task> GetCarouselImages(string service, string market) //{ // var selectedService = GetService(service); // var application = GetMarketApplication(market); // var webOperationContext = WebOperationContext.Current; // var httpContext = HttpContext.Current; // var lastModified = await _commonService.GetCarouselImageLastModifiedAsync(application); // if (webOperationContext != null) // { // CheckConditionalRetrieve(webOperationContext, lastModified); // SetCachability(httpContext); // webOperationContext.OutgoingResponse.LastModified = lastModified; // } // return selectedService.GetCarouselImages(application); //} [OperationContract] //[AspNetCacheProfile("CacheVeryShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/pages/{pageKey}")] public async Task GetPage(string service, string market, string pageKey) { var selectedService = GetService(service); var application = GetMarketApplication(market); var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; //var lastModified = await _commonService.GetCarouselImageLastModifiedAsync(application); //if (webOperationContext != null) //{ // CheckConditionalRetrieve(webOperationContext, lastModified); // SetCachability(httpContext); // webOperationContext.OutgoingResponse.LastModified = lastModified; //} var page = _commonService.GetPage(application, selectedService.ServiceType, pageKey); if (page == null) throw new WebFaultException(HttpStatusCode.NotFound); return GetAsJsonMessage(page, webOperationContext); } [OperationContract] //[AspNetCacheProfile("CacheVeryShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "{service}/{market}/pages/")] public async Task GetPages(string service, string market) { var selectedService = GetService(service); var application = GetMarketApplication(market); var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; //var lastModified = await _commonService.GetCarouselImageLastModifiedAsync(application); //if (webOperationContext != null) //{ // CheckConditionalRetrieve(webOperationContext, lastModified); // SetCachability(httpContext); // webOperationContext.OutgoingResponse.LastModified = lastModified; //} var page = _commonService.GetPages(application, selectedService.ServiceType); if (page == null) throw new WebFaultException(HttpStatusCode.NotFound); return GetAsJsonMessage(page, webOperationContext); } [OperationContract] [AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "spotify/availableRegions")] public async Task GetAvailableRegions() { var availableRegions = await _spotifyService.GetAvailableRegionsAsync(); var regionDic = availableRegions.Select(r => new RegionMarketMappingViewModel() { MarketId = _applicationInstanceManager.GetRegularApplicationByCountrySpecialFallback(r).ID, Region = r.ToLowerInvariant() }).ToDictionary(k=> k.Region, v=> v.MarketId); return GetAsJsonMessage(regionDic, WebOperationContext.Current); } [OperationContract] [AspNetCacheProfile("CacheShortTime")] [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "spotify/SonyMobileWhatsNewPlaylists?maxResults={maxResults}&locale={locale}&sortOrder={sortOrder}")] public async Task> GetSonyMobileWhatsNewPlaylists(int? maxResults, string locale, PlaylistSortOrder sortOrder) { var webOperationContext = WebOperationContext.Current; var httpContext = HttpContext.Current; var application = GetApplicationByCountrySpecialSonyMobileFallback(locale); if (application == null) return new List(); var lastModified = await _commonService.GetPlaylistLastModifiedDateAsync(application); if (webOperationContext != null) { CheckConditionalRetrieve(webOperationContext, lastModified); if (httpContext != null) httpContext.Response.Cache.SetCacheability(HttpCacheability.Public); //SetCachability(httpContext); webOperationContext.OutgoingResponse.LastModified = lastModified; } var playlists = await _spotifyService.GetPlaylistsAsync(application, lastModified); var playlistToInclude = playlists.Where(p => p.PriorityLevel == 4 || p.PriorityLevel == 5); if (!string.IsNullOrWhiteSpace(locale)) { playlistToInclude = playlistToInclude.Where(p => p.CountryCode != null && p.CountryCode.Equals(locale, StringComparison.InvariantCultureIgnoreCase)); } if (playlistToInclude.Count() <= 10) { var fallbackPlaylists = await _spotifyService.GetPlaylistsAsync(_applicationInstanceManager.GetFallbackApplication(), lastModified); playlistToInclude = fallbackPlaylists.Where(p => p.PriorityLevel == 4 || p.PriorityLevel == 5); //playlistToInclude = playlistToInclude.Where(p => p.CountryCode != null && p.CountryCode.Equals("_GL", StringComparison.InvariantCultureIgnoreCase)); } switch (sortOrder) { case PlaylistSortOrder.Popularity: playlistToInclude = playlistToInclude.OrderByDescending(p => p.Popularity); break; case PlaylistSortOrder.Followers: playlistToInclude = playlistToInclude.OrderByDescending(p => p.Followers); break; case PlaylistSortOrder.LatestUpdated: playlistToInclude = playlistToInclude.OrderByDescending(p => p.TracksLastAdded); break; } if (maxResults.HasValue) { playlistToInclude = playlistToInclude.Take(maxResults.Value); } return playlistToInclude.ToList(); } private Application GetApplicationByCountrySpecialSonyMobileFallback(string countryCode) { var apps = _applicationInstanceManager.GetApplications().Where(a => !a.WorkoutMarket).Where(a=> a.Services != null && a.Services.Contains("spotify", StringComparer.InvariantCultureIgnoreCase)).ToList(); var app = apps.FirstOrDefault(a => a.SpotifyRegionCode.Equals(countryCode, StringComparison.InvariantCultureIgnoreCase)); if (app != null && app.SpotifyRegionCode == "MX" && !app.Active) return GetApplicationByCountrySpecialSonyMobileFallback("ES"); if (app != null && app.Active) return app; if (_applicationInstanceManager.IsCentralAmericanAndCaribbeanCountry(countryCode)) { app = apps.FirstOrDefault(a => a.SpotifyRegionCode.Equals("CA&C", StringComparison.InvariantCultureIgnoreCase)); } if (_applicationInstanceManager.IsLatinCountry(countryCode)) { app = apps.FirstOrDefault(a => a.SpotifyRegionCode.Equals("Latin", StringComparison.InvariantCultureIgnoreCase)); } if (app != null) { if (app.Active) { return app; } else { return _applicationInstanceManager.GetFallbackApplication(); } } return null; } public enum PlaylistSortOrder { Popularity, Followers, LatestUpdated, } private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Formatting = Formatting.None, }; private static Message GetAsJsonMessage(T obj, WebOperationContext webOperationContext) { // use JSON.NET to serialize the response data string body = JsonConvert.SerializeObject(obj, JsonSerializerSettings); return webOperationContext.CreateTextResponse(body, "application/json; charset=utf-8", new UTF8Encoding(false)); } private static double? HandleChangedFormat(double? filterParameter) { return filterParameter.HasValue ? (double?)filterParameter.Value / 100.0 : null; } //We have copied the implementation from WCF and changed it to use the specified WebOperationContext since we need to capture it to prevent async errors. private void CheckConditionalRetrieve(WebOperationContext webOperationContext, DateTime lastModified) { DateTime? ifModifiedSince = webOperationContext.IncomingRequest.IfModifiedSince; if (ifModifiedSince.HasValue && lastModified != DateTime.MinValue) { long ticksDifference = lastModified.ToUniversalTime().Ticks - ifModifiedSince.Value.ToUniversalTime().Ticks; if (ticksDifference < TimeSpan.TicksPerSecond) { webOperationContext.OutgoingResponse.LastModified = lastModified; throw new WebFaultException(HttpStatusCode.NotModified); } } } } }