From b133fa95856f595a76b0fb8d458f68e58de85f8a Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Sat, 1 Jun 2013 23:41:30 -0700 Subject: [PATCH] Added populate lazy-loading extensions. --- NzbDrone.Api/Episodes/EpisodeModule.cs | 12 ++++- NzbDrone.Api/Extensions/LazyExtensions.cs | 53 ++++++++++++++++++++++ NzbDrone.Api/NzbDrone.Api.csproj | 4 ++ NzbDrone.Api/Properties/AssemblyInfo.cs | 26 ----------- NzbDrone.Core/Datastore/BasicRepository.cs | 13 ++++-- 5 files changed, 75 insertions(+), 33 deletions(-) create mode 100644 NzbDrone.Api/Extensions/LazyExtensions.cs diff --git a/NzbDrone.Api/Episodes/EpisodeModule.cs b/NzbDrone.Api/Episodes/EpisodeModule.cs index befa69c1c..e2d4e23d5 100644 --- a/NzbDrone.Api/Episodes/EpisodeModule.cs +++ b/NzbDrone.Api/Episodes/EpisodeModule.cs @@ -1,17 +1,22 @@ using System.Collections.Generic; using NzbDrone.Api.REST; +using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Tv; +using NzbDrone.Api.Extensions; +using System.Linq; namespace NzbDrone.Api.Episodes { public class EpisodeModule : NzbDroneRestModule { private readonly IEpisodeService _episodeService; + private readonly MediaFileRepository _mediaFileRepository; - public EpisodeModule(IEpisodeService episodeService) + public EpisodeModule(IEpisodeService episodeService, MediaFileRepository mediaFileRepository) : base("/episodes") { _episodeService = episodeService; + _mediaFileRepository = mediaFileRepository; GetResourceAll = GetEpisodes; } @@ -25,7 +30,10 @@ namespace NzbDrone.Api.Episodes throw new BadRequestException("seriesId is missing"); } - return ToListResource(() => _episodeService.GetEpisodeBySeries(seriesId.Value)); + var resource = ToListResource(() => _episodeService.GetEpisodeBySeries(seriesId.Value)) + .LoadSubtype(e => e.EpisodeFileId, _mediaFileRepository); + + return resource.ToList(); } } } \ No newline at end of file diff --git a/NzbDrone.Api/Extensions/LazyExtensions.cs b/NzbDrone.Api/Extensions/LazyExtensions.cs new file mode 100644 index 000000000..32852a26c --- /dev/null +++ b/NzbDrone.Api/Extensions/LazyExtensions.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using NzbDrone.Api.REST; +using NzbDrone.Common.Cache; +using NzbDrone.Core.Datastore; + +namespace NzbDrone.Api.Extensions +{ + public static class LazyExtensions + { + private static readonly ICached SetterCache = new Cached(); + + public static IEnumerable LoadSubtype(this IEnumerable parents, Func foreignKeySelector, IBasicRepository childRepository) + where TChild : ModelBase, new() + where TParent : RestResource + { + var parentList = parents.Where(p => foreignKeySelector(p) != 0).ToList(); + + if (!parentList.Any()) + { + return parents; + } + + var ids = parentList.Select(foreignKeySelector).Distinct(); + var childDictionary = childRepository.Get(ids).ToDictionary(child => child.Id, child => child); + + var childSetter = GetChildSetter(); + + foreach (var episode in parentList) + { + childSetter.Invoke(episode, new object[] { childDictionary[foreignKeySelector(episode)] }); + } + + return parents; + } + + + private static MethodInfo GetChildSetter() + where TChild : ModelBase + where TParent : RestResource + { + var key = typeof(TChild).FullName + typeof(TParent).FullName; + + return SetterCache.Get(key, () => + { + var property = typeof(TParent).GetProperties().Single(c => c.PropertyType == typeof(TChild)); + return property.GetSetMethod(); + }); + } + } +} diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index 4a2db59b2..da25c790c 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -86,6 +86,9 @@ + + Properties\SharedAssemblyInfo.cs + @@ -111,6 +114,7 @@ + diff --git a/NzbDrone.Api/Properties/AssemblyInfo.cs b/NzbDrone.Api/Properties/AssemblyInfo.cs index 812afcebc..6101dcac6 100644 --- a/NzbDrone.Api/Properties/AssemblyInfo.cs +++ b/NzbDrone.Api/Properties/AssemblyInfo.cs @@ -2,36 +2,10 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. [assembly: AssemblyTitle("NzbDrone.Api")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NzbDrone.Api")] -[assembly: AssemblyCopyright("Copyright © 2012")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("4c0922d7-979e-4ff7-b44b-b8ac2100eeb5")] -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NzbDrone.Core/Datastore/BasicRepository.cs b/NzbDrone.Core/Datastore/BasicRepository.cs index 0a9bdb453..7ec135748 100644 --- a/NzbDrone.Core/Datastore/BasicRepository.cs +++ b/NzbDrone.Core/Datastore/BasicRepository.cs @@ -6,6 +6,7 @@ using Marr.Data; using Marr.Data.QGen; using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore.Events; +using NzbDrone.Common; namespace NzbDrone.Core.Datastore @@ -79,12 +80,14 @@ namespace NzbDrone.Core.Datastore public IEnumerable Get(IEnumerable ids) { - var idList = ids.ToList(); - var result = Query.Where(String.Format("Id IN ({0})", String.Join(",", idList))).ToList(); - var resultCount = result.Count; + var query = String.Format("Id IN ({0})", String.Join(",", ids)); - if (resultCount != idList.Count || result.Select(r => r.Id).Distinct().Count() != resultCount) - throw new InvalidOperationException("Unexpected result from query"); + var result = Query.Where(query).ToList(); + + if (result.Count != ids.Count()) + { + throw new ApplicationException("Expected query to return {0} rows but returned {1}".Inject(ids.Count(), result.Count)); + } return result; }