Browse Source

Fix warnings, improve performance (#1665)

* Fix warnings, improve performance

`QueryResult.Items` is now a `IReadOnlyList` so we don't need to
allocate a new `Array` when we have a `List` (and `Items` shouldn't need to
be mutable anyway)

* Update Providers .csproj to latest C#

* Remove extra newline from DtoService.cs

* Remove extra newline from UserLibraryService.cs
Bond-009 5 năm trước cách đây
mục cha
commit
ee637e8fec
37 tập tin đã thay đổi với 308 bổ sung218 xóa
  1. 6 5
      Emby.Dlna/ContentDirectory/ControlHandler.cs
  2. 1 1
      Emby.Server.Implementations/Activity/ActivityRepository.cs
  3. 5 5
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  4. 52 58
      Emby.Server.Implementations/Dto/DtoService.cs
  5. 12 5
      Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
  6. 2 3
      Emby.Server.Implementations/Library/LibraryManager.cs
  7. 14 16
      Emby.Server.Implementations/Library/UserViewManager.cs
  8. 11 7
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  9. 1 1
      Emby.Server.Implementations/Services/ResponseHelper.cs
  10. 17 22
      Emby.Server.Implementations/Services/ServiceExec.cs
  11. 9 5
      Emby.Server.Implementations/Services/UrlExtensions.cs
  12. 1 1
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  13. 1 1
      MediaBrowser.Api/Movies/MoviesService.cs
  14. 2 4
      MediaBrowser.Api/PlaylistService.cs
  15. 0 5
      MediaBrowser.Api/SuggestionsService.cs
  16. 3 3
      MediaBrowser.Api/TvShowsService.cs
  17. 0 5
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  18. 13 4
      MediaBrowser.Api/UserLibrary/UserLibraryService.cs
  19. 7 3
      MediaBrowser.Api/UserService.cs
  20. 17 0
      MediaBrowser.Common/Extensions/CollectionExtensions.cs
  21. 1 1
      MediaBrowser.Common/Net/CustomHeaderNames.cs
  22. 1 1
      MediaBrowser.Controller/Dto/IDtoService.cs
  23. 4 4
      MediaBrowser.Controller/Entities/BaseItem.cs
  24. 7 2
      MediaBrowser.Controller/Entities/Extensions.cs
  25. 9 9
      MediaBrowser.Controller/Entities/Folder.cs
  26. 61 9
      MediaBrowser.Controller/Entities/IHasTrailers.cs
  27. 7 3
      MediaBrowser.Controller/Entities/Movies/BoxSet.cs
  28. 5 2
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  29. 5 2
      MediaBrowser.Controller/Entities/TV/Episode.cs
  30. 5 2
      MediaBrowser.Controller/Entities/TV/Series.cs
  31. 2 2
      MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
  32. 1 1
      MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
  33. 1 1
      MediaBrowser.Model/Dto/BaseItemDto.cs
  34. 2 1
      MediaBrowser.Model/Dto/RecommendationDto.cs
  35. 5 2
      MediaBrowser.Model/Querying/QueryResult.cs
  36. 17 21
      MediaBrowser.Providers/Manager/ProviderUtils.cs
  37. 1 1
      MediaBrowser.Providers/MediaBrowser.Providers.csproj

+ 6 - 5
Emby.Dlna/ContentDirectory/ControlHandler.cs

@@ -289,7 +289,7 @@ namespace Emby.Dlna.ContentDirectory
                     var childrenResult = GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount);
                     totalCount = childrenResult.TotalRecordCount;
 
-                    provided = childrenResult.Items.Length;
+                    provided = childrenResult.Items.Count;
 
                     foreach (var i in childrenResult.Items)
                     {
@@ -309,6 +309,7 @@ namespace Emby.Dlna.ContentDirectory
                         }
                     }
                 }
+
                 writer.WriteFullEndElement();
                 //writer.WriteEndDocument();
             }
@@ -386,7 +387,7 @@ namespace Emby.Dlna.ContentDirectory
 
                 totalCount = childrenResult.TotalRecordCount;
 
-                provided = childrenResult.Items.Length;
+                provided = childrenResult.Items.Count;
 
                 var dlnaOptions = _config.GetDlnaConfiguration();
 
@@ -677,7 +678,7 @@ namespace Emby.Dlna.ContentDirectory
 
             return new QueryResult<ServerItem>
             {
-                Items = list.ToArray(),
+                Items = list,
                 TotalRecordCount = list.Count
             };
         }
@@ -755,7 +756,7 @@ namespace Emby.Dlna.ContentDirectory
 
             return new QueryResult<ServerItem>
             {
-                Items = list.ToArray(),
+                Items = list,
                 TotalRecordCount = list.Count
             };
         }
@@ -860,7 +861,7 @@ namespace Emby.Dlna.ContentDirectory
 
             return new QueryResult<ServerItem>
             {
-                Items = list.ToArray(),
+                Items = list,
                 TotalRecordCount = list.Count
             };
         }

+ 1 - 1
Emby.Server.Implementations/Activity/ActivityRepository.cs

@@ -247,7 +247,7 @@ namespace Emby.Server.Implementations.Activity
                     ReadTransactionMode);
             }
 
-            result.Items = list.ToArray();
+            result.Items = list;
             return result;
         }
 

+ 5 - 5
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -2746,7 +2746,7 @@ namespace Emby.Server.Implementations.Data
                 var returnList = GetItemList(query);
                 return new QueryResult<BaseItem>
                 {
-                    Items = returnList.ToArray(),
+                    Items = returnList,
                     TotalRecordCount = returnList.Count
                 };
             }
@@ -2883,7 +2883,7 @@ namespace Emby.Server.Implementations.Data
             }
 
             LogQueryTime("GetItems", commandText, now);
-            result.Items = list.ToArray();
+            result.Items = list;
             return result;
         }
 
@@ -3161,7 +3161,7 @@ namespace Emby.Server.Implementations.Data
                 var returnList = GetItemIdsList(query);
                 return new QueryResult<Guid>
                 {
-                    Items = returnList.ToArray(),
+                    Items = returnList,
                     TotalRecordCount = returnList.Count
                 };
             }
@@ -3281,7 +3281,7 @@ namespace Emby.Server.Implementations.Data
 
             LogQueryTime("GetItemIds", commandText, now);
 
-            result.Items = list.ToArray();
+            result.Items = list;
             return result;
         }
 
@@ -5520,7 +5520,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
                 result.TotalRecordCount = list.Count;
             }
 
-            result.Items = list.ToArray();
+            result.Items = list;
 
             return result;
         }

+ 52 - 58
Emby.Server.Implementations/Dto/DtoService.cs

@@ -80,27 +80,25 @@ namespace Emby.Server.Implementations.Dto
             return GetBaseItemDto(item, options, user, owner);
         }
 
-        public BaseItemDto[] GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
-            => GetBaseItemDtos(items, items.Count, options, user, owner);
-
-        public BaseItemDto[] GetBaseItemDtos(IEnumerable<BaseItem> items, int itemCount, DtoOptions options, User user = null, BaseItem owner = null)
+        /// <inheritdoc />
+        public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
         {
-            var returnItems = new BaseItemDto[itemCount];
-            var programTuples = new List<Tuple<BaseItem, BaseItemDto>>();
-            var channelTuples = new List<Tuple<BaseItemDto, LiveTvChannel>>();
+            var returnItems = new BaseItemDto[items.Count];
+            var programTuples = new List<(BaseItem, BaseItemDto)>();
+            var channelTuples = new List<(BaseItemDto, LiveTvChannel)>();
 
-            var index = 0;
-            foreach (var item in items)
+            for (int index = 0; index < items.Count; index++)
             {
+                var item = items[index];
                 var dto = GetBaseItemDtoInternal(item, options, user, owner);
 
                 if (item is LiveTvChannel tvChannel)
                 {
-                    channelTuples.Add(new Tuple<BaseItemDto, LiveTvChannel>(dto, tvChannel));
+                    channelTuples.Add((dto, tvChannel));
                 }
                 else if (item is LiveTvProgram)
                 {
-                    programTuples.Add(new Tuple<BaseItem, BaseItemDto>(item, dto));
+                    programTuples.Add((item, dto));
                 }
 
                 if (item is IItemByName byName)
@@ -121,7 +119,6 @@ namespace Emby.Server.Implementations.Dto
                 }
 
                 returnItems[index] = dto;
-                index++;
             }
 
             if (programTuples.Count > 0)
@@ -140,33 +137,32 @@ namespace Emby.Server.Implementations.Dto
         public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
         {
             var dto = GetBaseItemDtoInternal(item, options, user, owner);
-            var tvChannel = item as LiveTvChannel;
-            if (tvChannel != null)
+            if (item is LiveTvChannel tvChannel)
             {
-                var list = new List<Tuple<BaseItemDto, LiveTvChannel>> { new Tuple<BaseItemDto, LiveTvChannel>(dto, tvChannel) };
+                var list = new List<(BaseItemDto, LiveTvChannel)>(1) { (dto, tvChannel) };
                 _livetvManager().AddChannelInfo(list, options, user);
             }
             else if (item is LiveTvProgram)
             {
-                var list = new List<Tuple<BaseItem, BaseItemDto>> { new Tuple<BaseItem, BaseItemDto>(item, dto) };
+                var list = new List<(BaseItem, BaseItemDto)>(1) { (item, dto) };
                 var task = _livetvManager().AddInfoToProgramDto(list, options.Fields, user);
                 Task.WaitAll(task);
             }
 
-            var byName = item as IItemByName;
-
-            if (byName != null)
+            if (item is IItemByName itemByName
+                && options.ContainsField(ItemFields.ItemCounts))
             {
-                if (options.ContainsField(ItemFields.ItemCounts))
-                {
-                    SetItemByNameInfo(item, dto, GetTaggedItems(byName, user, new DtoOptions(false)
-                    {
-                        EnableImages = false
-
-                    }), user);
-                }
-
-                return dto;
+                SetItemByNameInfo(
+                    item,
+                    dto,
+                    GetTaggedItems(
+                        itemByName,
+                        user,
+                        new DtoOptions(false)
+                        {
+                            EnableImages = false
+                        }),
+                    user);
             }
 
             return dto;
@@ -174,12 +170,12 @@ namespace Emby.Server.Implementations.Dto
 
         private static IList<BaseItem> GetTaggedItems(IItemByName byName, User user, DtoOptions options)
         {
-            return byName.GetTaggedItems(new InternalItemsQuery(user)
-            {
-                Recursive = true,
-                DtoOptions = options
-
-            });
+            return byName.GetTaggedItems(
+                new InternalItemsQuery(user)
+                {
+                    Recursive = true,
+                    DtoOptions = options
+                });
         }
 
         private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
@@ -222,8 +218,7 @@ namespace Emby.Server.Implementations.Dto
                 AttachUserSpecificInfo(dto, item, user, options);
             }
 
-            var hasMediaSources = item as IHasMediaSources;
-            if (hasMediaSources != null)
+            if (item is IHasMediaSources hasMediaSources)
             {
                 if (options.ContainsField(ItemFields.MediaSources))
                 {
@@ -769,14 +764,12 @@ namespace Emby.Server.Implementations.Dto
 
             dto.CriticRating = item.CriticRating;
 
-            var hasDisplayOrder = item as IHasDisplayOrder;
-            if (hasDisplayOrder != null)
+            if (item is IHasDisplayOrder hasDisplayOrder)
             {
                 dto.DisplayOrder = hasDisplayOrder.DisplayOrder;
             }
 
-            var hasCollectionType = item as IHasCollectionType;
-            if (hasCollectionType != null)
+            if (item is IHasCollectionType hasCollectionType)
             {
                 dto.CollectionType = hasCollectionType.CollectionType;
             }
@@ -1073,17 +1066,24 @@ namespace Emby.Server.Implementations.Dto
 
             if (options.ContainsField(ItemFields.LocalTrailerCount))
             {
+                int trailerCount = 0;
                 if (allExtras == null)
                 {
                     allExtras = item.GetExtras().ToArray();
                 }
 
-                dto.LocalTrailerCount = allExtras.Count(i => i.ExtraType.HasValue && i.ExtraType.Value == ExtraType.Trailer) + item.GetTrailers().Count();
+                trailerCount += allExtras.Count(i => i.ExtraType.HasValue && i.ExtraType.Value == ExtraType.Trailer);
+
+                if (item is IHasTrailers hasTrailers)
+                {
+                    trailerCount += hasTrailers.GetTrailerCount();
+                }
+
+                dto.LocalTrailerCount = trailerCount;
             }
 
             // Add EpisodeInfo
-            var episode = item as Episode;
-            if (episode != null)
+            if (item is Episode episode)
             {
                 dto.IndexNumberEnd = episode.IndexNumberEnd;
                 dto.SeriesName = episode.SeriesName;
@@ -1101,7 +1101,7 @@ namespace Emby.Server.Implementations.Dto
 
                 Series episodeSeries = null;
 
-                //if (options.ContainsField(ItemFields.SeriesPrimaryImage))
+                if (options.ContainsField(ItemFields.SeriesPrimaryImage))
                 {
                     episodeSeries = episodeSeries ?? episode.Series;
                     if (episodeSeries != null)
@@ -1121,8 +1121,7 @@ namespace Emby.Server.Implementations.Dto
             }
 
             // Add SeriesInfo
-            var series = item as Series;
-            if (series != null)
+            if (item is Series series)
             {
                 dto.AirDays = series.AirDays;
                 dto.AirTime = series.AirTime;
@@ -1130,8 +1129,7 @@ namespace Emby.Server.Implementations.Dto
             }
 
             // Add SeasonInfo
-            var season = item as Season;
-            if (season != null)
+            if (item is Season season)
             {
                 dto.SeriesName = season.SeriesName;
                 dto.SeriesId = season.SeriesId;
@@ -1147,7 +1145,7 @@ namespace Emby.Server.Implementations.Dto
                     }
                 }
 
-                //if (options.ContainsField(ItemFields.SeriesPrimaryImage))
+                if (options.ContainsField(ItemFields.SeriesPrimaryImage))
                 {
                     series = series ?? season.Series;
                     if (series != null)
@@ -1157,14 +1155,12 @@ namespace Emby.Server.Implementations.Dto
                 }
             }
 
-            var musicVideo = item as MusicVideo;
-            if (musicVideo != null)
+            if (item is MusicVideo musicVideo)
             {
                 SetMusicVideoProperties(dto, musicVideo);
             }
 
-            var book = item as Book;
-            if (book != null)
+            if (item is Book book)
             {
                 SetBookProperties(dto, book);
             }
@@ -1204,8 +1200,7 @@ namespace Emby.Server.Implementations.Dto
                 }
             }
 
-            var photo = item as Photo;
-            if (photo != null)
+            if (item is Photo photo)
             {
                 SetPhotoProperties(dto, photo);
             }
@@ -1224,8 +1219,7 @@ namespace Emby.Server.Implementations.Dto
 
         private BaseItem GetImageDisplayParent(BaseItem currentItem, BaseItem originalItem)
         {
-            var musicAlbum = currentItem as MusicAlbum;
-            if (musicAlbum != null)
+            if (currentItem is MusicAlbum musicAlbum)
             {
                 var artist = musicAlbum.GetMusicArtist(new DtoOptions(false));
                 if (artist != null)

+ 12 - 5
Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Net;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Security;
@@ -89,7 +90,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
                     AccessToken = token
                 });
 
-                var tokenInfo = result.Items.Length > 0 ? result.Items[0] : null;
+                var tokenInfo = result.Items.Count > 0 ? result.Items[0] : null;
 
                 if (tokenInfo != null)
                 {
@@ -190,17 +191,23 @@ namespace Emby.Server.Implementations.HttpServer.Security
         /// <returns>Dictionary{System.StringSystem.String}.</returns>
         private Dictionary<string, string> GetAuthorization(string authorizationHeader)
         {
-            if (authorizationHeader == null) return null;
+            if (authorizationHeader == null)
+            {
+                return null;
+            }
 
             var parts = authorizationHeader.Split(new[] { ' ' }, 2);
 
             // There should be at least to parts
-            if (parts.Length != 2) return null;
+            if (parts.Length != 2)
+            {
+                return null;
+            }
 
             var acceptedNames = new[] { "MediaBrowser", "Emby" };
 
             // It has to be a digest request
-            if (!acceptedNames.Contains(parts[0] ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+            if (!acceptedNames.Contains(parts[0], StringComparer.OrdinalIgnoreCase))
             {
                 return null;
             }
@@ -232,7 +239,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
                 return value;
             }
 
-            return System.Net.WebUtility.HtmlEncode(value);
+            return WebUtility.HtmlEncode(value);
         }
     }
 }

+ 2 - 3
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -1441,7 +1441,7 @@ namespace Emby.Server.Implementations.Library
 
             return new QueryResult<BaseItem>
             {
-                Items = list.ToArray()
+                Items = list
             };
         }
 
@@ -1977,8 +1977,7 @@ namespace Emby.Server.Implementations.Library
 
         public LibraryOptions GetLibraryOptions(BaseItem item)
         {
-            var collectionFolder = item as CollectionFolder;
-            if (collectionFolder == null)
+            if (!(item is CollectionFolder collectionFolder))
             {
                 collectionFolder = GetCollectionFolders(item)
                    .OfType<CollectionFolder>()

+ 14 - 16
Emby.Server.Implementations/Library/UserViewManager.cs

@@ -224,7 +224,7 @@ namespace Emby.Server.Implementations.Library
             return list;
         }
 
-        private List<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request, DtoOptions options)
+        private IReadOnlyList<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request, DtoOptions options)
         {
             var parentId = request.ParentId;
 
@@ -236,24 +236,22 @@ namespace Emby.Server.Implementations.Library
             if (!parentId.Equals(Guid.Empty))
             {
                 var parentItem = _libraryManager.GetItemById(parentId);
-                var parentItemChannel = parentItem as Channel;
-                if (parentItemChannel != null)
+                if (parentItem is Channel parentItemChannel)
                 {
-                    return _channelManager.GetLatestChannelItemsInternal(new InternalItemsQuery(user)
-                    {
-                        ChannelIds = new[] { parentId },
-                        IsPlayed = request.IsPlayed,
-                        StartIndex = request.StartIndex,
-                        Limit = request.Limit,
-                        IncludeItemTypes = request.IncludeItemTypes,
-                        EnableTotalRecordCount = false
-
-
-                    }, CancellationToken.None).Result.Items.ToList();
+                    return _channelManager.GetLatestChannelItemsInternal(
+                        new InternalItemsQuery(user)
+                        {
+                            ChannelIds = new[] { parentId },
+                            IsPlayed = request.IsPlayed,
+                            StartIndex = request.StartIndex,
+                            Limit = request.Limit,
+                            IncludeItemTypes = request.IncludeItemTypes,
+                            EnableTotalRecordCount = false
+                        },
+                        CancellationToken.None).Result.Items;
                 }
 
-                var parent = parentItem as Folder;
-                if (parent != null)
+                if (parentItem is Folder parent)
                 {
                     parents.Add(parent);
                 }

+ 11 - 7
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -881,7 +881,7 @@ namespace Emby.Server.Implementations.LiveTv
             }
 
             var programList = _libraryManager.QueryItems(internalQuery).Items;
-            var totalCount = programList.Length;
+            var totalCount = programList.Count;
 
             var orderedPrograms = programList.Cast<LiveTvProgram>().OrderBy(i => i.StartDate.Date);
 
@@ -969,8 +969,8 @@ namespace Emby.Server.Implementations.LiveTv
             var timers = new Dictionary<string, List<TimerInfo>>();
             var seriesTimers = new Dictionary<string, List<SeriesTimerInfo>>();
 
-            TimerInfo[] timerList = null;
-            SeriesTimerInfo[] seriesTimerList = null;
+            IReadOnlyList<TimerInfo> timerList = null;
+            IReadOnlyList<SeriesTimerInfo> seriesTimerList = null;
 
             foreach (var programTuple in programs)
             {
@@ -1296,6 +1296,7 @@ namespace Emby.Server.Implementations.LiveTv
         }
 
         private const int MaxGuideDays = 14;
+
         private double GetGuideDays()
         {
             var config = GetConfiguration();
@@ -1340,6 +1341,7 @@ namespace Emby.Server.Implementations.LiveTv
                     excludeItemTypes.Add(typeof(Movie).Name);
                 }
             }
+
             if (query.IsSeries.HasValue)
             {
                 if (query.IsSeries.Value)
@@ -1351,10 +1353,12 @@ namespace Emby.Server.Implementations.LiveTv
                     excludeItemTypes.Add(typeof(Episode).Name);
                 }
             }
+
             if (query.IsSports ?? false)
             {
                 genres.Add("Sports");
             }
+
             if (query.IsKids ?? false)
             {
                 genres.Add("Kids");
@@ -1400,20 +1404,20 @@ namespace Emby.Server.Implementations.LiveTv
 
             if (query.IsInProgress ?? false)
             {
-                //TODO Fix The co-variant conversion between Video[] and BaseItem[], this can generate runtime issues.
+                // TODO: Fix The co-variant conversion between Video[] and BaseItem[], this can generate runtime issues.
                 result.Items = result
                     .Items
                     .OfType<Video>()
                     .Where(i => !i.IsCompleteMedia)
                     .ToArray();
 
-                result.TotalRecordCount = result.Items.Length;
+                result.TotalRecordCount = result.Items.Count;
             }
 
             return result;
         }
 
-        public Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> tuples, ItemFields[] fields, User user = null)
+        public Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> tuples, ItemFields[] fields, User user = null)
         {
             var programTuples = new List<Tuple<BaseItemDto, string, string>>();
             var hasChannelImage = fields.Contains(ItemFields.ChannelImage);
@@ -1877,7 +1881,7 @@ namespace Emby.Server.Implementations.LiveTv
             return _libraryManager.GetItemById(internalChannelId);
         }
 
-        public void AddChannelInfo(List<Tuple<BaseItemDto, LiveTvChannel>> tuples, DtoOptions options, User user)
+        public void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> tuples, DtoOptions options, User user)
         {
             var now = DateTime.UtcNow;
 

+ 1 - 1
Emby.Server.Implementations/Services/ResponseHelper.cs

@@ -6,8 +6,8 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Emby.Server.Implementations.HttpServer;
-using Microsoft.AspNetCore.Http;
 using MediaBrowser.Model.Services;
+using Microsoft.AspNetCore.Http;
 
 namespace Emby.Server.Implementations.Services
 {

+ 17 - 22
Emby.Server.Implementations/Services/ServiceExec.cs

@@ -87,8 +87,7 @@ namespace Emby.Server.Implementations.Services
 
                 var response = actionContext.ServiceAction(instance, requestDto);
 
-                var taskResponse = response as Task;
-                if (taskResponse != null)
+                if (response is Task taskResponse)
                 {
                     return GetTaskResult(taskResponse);
                 }
@@ -104,8 +103,7 @@ namespace Emby.Server.Implementations.Services
         {
             try
             {
-                var taskObject = task as Task<object>;
-                if (taskObject != null)
+                if (task is Task<object> taskObject)
                 {
                     return await taskObject.ConfigureAwait(false);
                 }
@@ -136,7 +134,7 @@ namespace Emby.Server.Implementations.Services
             }
             catch (TypeAccessException)
             {
-                return null; //return null for void Task's
+                return null; // return null for void Task's
             }
         }
 
@@ -155,29 +153,22 @@ namespace Emby.Server.Implementations.Services
                     Id = ServiceMethod.Key(serviceType, actionName, requestType.GetMethodName())
                 };
 
-                try
-                {
-                    actionCtx.ServiceAction = CreateExecFn(serviceType, requestType, mi);
-                }
-                catch
-                {
-                    //Potential problems with MONO, using reflection for fallback
-                    actionCtx.ServiceAction = (service, request) =>
-                                              mi.Invoke(service, new[] { request });
-                }
+                actionCtx.ServiceAction = CreateExecFn(serviceType, requestType, mi);
 
                 var reqFilters = new List<IHasRequestFilter>();
 
                 foreach (var attr in mi.GetCustomAttributes(true))
                 {
-                    var hasReqFilter = attr as IHasRequestFilter;
-
-                    if (hasReqFilter != null)
+                    if (attr is IHasRequestFilter hasReqFilter)
+                    {
                         reqFilters.Add(hasReqFilter);
+                    }
                 }
 
                 if (reqFilters.Count > 0)
+                {
                     actionCtx.RequestFilters = reqFilters.OrderBy(i => i.Priority).ToArray();
+                }
 
                 actions.Add(actionCtx);
             }
@@ -198,15 +189,19 @@ namespace Emby.Server.Implementations.Services
 
             if (mi.ReturnType != typeof(void))
             {
-                var executeFunc = Expression.Lambda<ActionInvokerFn>
-                (callExecute, serviceParam, requestDtoParam).Compile();
+                var executeFunc = Expression.Lambda<ActionInvokerFn>(
+                    callExecute,
+                    serviceParam,
+                    requestDtoParam).Compile();
 
                 return executeFunc;
             }
             else
             {
-                var executeFunc = Expression.Lambda<VoidActionInvokerFn>
-                (callExecute, serviceParam, requestDtoParam).Compile();
+                var executeFunc = Expression.Lambda<VoidActionInvokerFn>(
+                    callExecute,
+                    serviceParam,
+                    requestDtoParam).Compile();
 
                 return (service, request) =>
                 {

+ 9 - 5
Emby.Server.Implementations/Services/UrlExtensions.cs

@@ -12,10 +12,10 @@ namespace Emby.Server.Implementations.Services
     {
         public static string GetMethodName(this Type type)
         {
-            var typeName = type.FullName != null //can be null, e.g. generic types
-                ? LeftPart(type.FullName, "[[")   //Generic Fullname
-                    .Replace(type.Namespace + ".", "") //Trim Namespaces
-                    .Replace("+", ".") //Convert nested into normal type
+            var typeName = type.FullName != null // can be null, e.g. generic types
+                ? LeftPart(type.FullName, "[[")   // Generic Fullname
+                    .Replace(type.Namespace + ".", string.Empty) // Trim Namespaces
+                    .Replace("+", ".") // Convert nested into normal type
                 : type.Name;
 
             return type.IsGenericParameter ? "'" + typeName : typeName;
@@ -23,7 +23,11 @@ namespace Emby.Server.Implementations.Services
 
         private static string LeftPart(string strVal, string needle)
         {
-            if (strVal == null) return null;
+            if (strVal == null)
+            {
+                return null;
+            }
+
             var pos = strVal.IndexOf(needle, StringComparison.OrdinalIgnoreCase);
             return pos == -1
                 ? strVal

+ 1 - 1
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -741,7 +741,7 @@ namespace MediaBrowser.Api.LiveTv
             var result = new QueryResult<BaseItemDto>
             {
                 Items = returnArray,
-                TotalRecordCount = returnArray.Length
+                TotalRecordCount = returnArray.Count
             };
 
             return ToOptimizedResult(result);

+ 1 - 1
MediaBrowser.Api/Movies/MoviesService.cs

@@ -243,7 +243,7 @@ namespace MediaBrowser.Api.Movies
                 }
             }
 
-            return categories.OrderBy(i => i.RecommendationType).ThenBy(i => Guid.NewGuid());
+            return categories.OrderBy(i => i.RecommendationType);
         }
 
         private IEnumerable<RecommendationDto> GetWithDirector(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type)

+ 2 - 4
MediaBrowser.Api/PlaylistService.cs

@@ -189,11 +189,9 @@ namespace MediaBrowser.Api
 
             var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2).ToList(), dtoOptions, user);
 
-            var index = 0;
-            foreach (var item in dtos)
+            for (int index = 0; index < dtos.Count; index++)
             {
-                item.PlaylistItemId = items[index].Item1.Id;
-                index++;
+                dtos[index].PlaylistItemId = items[index].Item1.Id;
             }
 
             var result = new QueryResult<BaseItemDto>

+ 0 - 5
MediaBrowser.Api/SuggestionsService.cs

@@ -61,11 +61,6 @@ namespace MediaBrowser.Api
 
             var dtoList = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user);
 
-            if (dtoList == null)
-            {
-                throw new InvalidOperationException("GetBaseItemDtos returned null");
-            }
-
             return new QueryResult<BaseItemDto>
             {
                 TotalRecordCount = result.TotalRecordCount,

+ 3 - 3
MediaBrowser.Api/TvShowsService.cs

@@ -382,13 +382,13 @@ namespace MediaBrowser.Api
                 throw new ResourceNotFoundException("Series not found");
             }
 
-            var seasons = (series.GetItemList(new InternalItemsQuery(user)
+            var seasons = series.GetItemList(new InternalItemsQuery(user)
             {
                 IsMissing = request.IsMissing,
                 IsSpecialSeason = request.IsSpecialSeason,
                 AdjacentTo = request.AdjacentTo
 
-            }));
+            });
 
             var dtoOptions = GetDtoOptions(_authContext, request);
 
@@ -396,7 +396,7 @@ namespace MediaBrowser.Api
 
             return new QueryResult<BaseItemDto>
             {
-                TotalRecordCount = returnItems.Length,
+                TotalRecordCount = returnItems.Count,
                 Items = returnItems
             };
         }

+ 0 - 5
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -175,11 +175,6 @@ namespace MediaBrowser.Api.UserLibrary
 
             var dtoList = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user);
 
-            if (dtoList == null)
-            {
-                throw new InvalidOperationException("GetBaseItemDtos returned null");
-            }
-
             return new QueryResult<BaseItemDto>
             {
                 TotalRecordCount = result.TotalRecordCount,

+ 13 - 4
MediaBrowser.Api/UserLibrary/UserLibraryService.cs

@@ -2,6 +2,7 @@ using System;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -367,12 +368,20 @@ namespace MediaBrowser.Api.UserLibrary
             var dtoOptions = GetDtoOptions(_authContext, request);
 
             var dtosExtras = item.GetExtras(new[] { ExtraType.Trailer })
-                .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
+                .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))
+                .ToArray();
 
-            var dtosTrailers = item.GetTrailers()
-                .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
+            if (item is IHasTrailers hasTrailers)
+            {
+                var trailers = hasTrailers.GetTrailers();
+                var dtosTrailers = _dtoService.GetBaseItemDtos(trailers, dtoOptions, user, item);
+                var allTrailers = new BaseItemDto[dtosExtras.Length + dtosTrailers.Count];
+                dtosExtras.CopyTo(allTrailers, 0);
+                dtosTrailers.CopyTo(allTrailers, dtosExtras.Length);
+                return ToOptimizedResult(allTrailers);
+            }
 
-            return ToOptimizedResult(dtosExtras.Concat(dtosTrailers).ToArray());
+            return ToOptimizedResult(dtosExtras);
         }
 
         /// <summary>

+ 7 - 3
MediaBrowser.Api/UserService.cs

@@ -248,9 +248,14 @@ namespace MediaBrowser.Api
         private readonly INetworkManager _networkManager;
         private readonly IDeviceManager _deviceManager;
         private readonly IAuthorizationContext _authContext;
-        private readonly ILogger _logger;
 
-        public UserService(IUserManager userManager, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager, IDeviceManager deviceManager, IAuthorizationContext authContext, ILoggerFactory loggerFactory)
+        public UserService(
+            IUserManager userManager,
+            ISessionManager sessionMananger,
+            IServerConfigurationManager config,
+            INetworkManager networkManager,
+            IDeviceManager deviceManager,
+            IAuthorizationContext authContext)
         {
             _userManager = userManager;
             _sessionMananger = sessionMananger;
@@ -258,7 +263,6 @@ namespace MediaBrowser.Api
             _networkManager = networkManager;
             _deviceManager = deviceManager;
             _authContext = authContext;
-            _logger = loggerFactory.CreateLogger(nameof(UserService));
         }
 
         public object Get(GetPublicUsers request)

+ 17 - 0
MediaBrowser.Common/Extensions/CollectionExtensions.cs

@@ -10,5 +10,22 @@ namespace MediaBrowser.Common.Extensions
             dictionary.TryGetValue(key, out var ret);
             return ret;
         }
+
+        // REVIEW: Inline?
+        /// <summary>
+        /// Copies all the elements of the current collection to the specified list
+        /// starting at the specified destination array index. The index is specified as a 32-bit integer.
+        /// </summary>
+        /// <param name="source">The current collection that is the source of the elements.</param>
+        /// <param name="destination">The list that is the destination of the elements copied from the current collection.</param>
+        /// <param name="index">A 32-bit integer that represents the index in <c>destination</c> at which copying begins.</param>
+        /// <typeparam name="T"></typeparam>
+        public static void CopyTo<T>(this IReadOnlyCollection<T> source, IList<T> destination, int index = 0)
+        {
+            foreach (T item in source)
+            {
+                destination[index++] = item;
+            }
+        }
     }
 }

+ 1 - 1
MediaBrowser.Common/Net/CustomHeaderNames.cs

@@ -8,4 +8,4 @@ namespace MediaBrowser.Common.Net
         public const string XForwardedProto = "X-Forwarded-Proto";
         public const string XRealIP = "X-Real-IP";
     }
-}
+}

+ 1 - 1
MediaBrowser.Controller/Dto/IDtoService.cs

@@ -57,7 +57,7 @@ namespace MediaBrowser.Controller.Dto
         /// <param name="options">The options.</param>
         /// <param name="user">The user.</param>
         /// <param name="owner">The owner.</param>
-        BaseItemDto[] GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null);
+        IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null);
 
         /// <summary>
         /// Gets the item by name dto.

+ 4 - 4
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -2871,16 +2871,16 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the remote trailers.
         /// </summary>
         /// <value>The remote trailers.</value>
-        public MediaUrl[] RemoteTrailers { get; set; }
+        public IReadOnlyList<MediaUrl> RemoteTrailers { get; set; }
 
         public IEnumerable<BaseItem> GetExtras()
         {
             return ExtraIds.Select(LibraryManager.GetItemById).Where(i => i != null).OrderBy(i => i.SortName);
         }
 
-        public IEnumerable<BaseItem> GetExtras(ExtraType[] extraTypes)
+        public IEnumerable<BaseItem> GetExtras(IReadOnlyCollection<ExtraType> extraTypes)
         {
-            return ExtraIds.Select(LibraryManager.GetItemById).Where(i => i != null && extraTypes.Contains(i.ExtraType.Value)).OrderBy(i => i.SortName);
+            return ExtraIds.Select(LibraryManager.GetItemById).Where(i => i != null && extraTypes.Contains(i.ExtraType.Value));
         }
 
         public IEnumerable<BaseItem> GetTrailers()
@@ -2908,7 +2908,7 @@ namespace MediaBrowser.Controller.Entities
         }
 
         // Possible types of extra videos
-        public static ExtraType[] DisplayExtraTypes = new[] { Model.Entities.ExtraType.BehindTheScenes, Model.Entities.ExtraType.Clip, Model.Entities.ExtraType.DeletedScene, Model.Entities.ExtraType.Interview, Model.Entities.ExtraType.Sample, Model.Entities.ExtraType.Scene };
+        public static readonly IReadOnlyCollection<ExtraType> DisplayExtraTypes = new[] { Model.Entities.ExtraType.BehindTheScenes, Model.Entities.ExtraType.Clip, Model.Entities.ExtraType.DeletedScene, Model.Entities.ExtraType.Interview, Model.Entities.ExtraType.Sample, Model.Entities.ExtraType.Scene };
 
         public virtual bool SupportsExternalTransfer => false;
     }

+ 7 - 2
MediaBrowser.Controller/Entities/Extensions.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Linq;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.Entities
@@ -28,13 +29,17 @@ namespace MediaBrowser.Controller.Entities
                     Url = url
                 };
 
-                if (item.RemoteTrailers.Length == 0)
+                if (item.RemoteTrailers.Count == 0)
                 {
                     item.RemoteTrailers = new[] { mediaUrl };
                 }
                 else
                 {
-                    item.RemoteTrailers = item.RemoteTrailers.Concat(new[] { mediaUrl }).ToArray();
+                    var oldIds = item.RemoteTrailers;
+                    var newIds = new MediaUrl[oldIds.Count + 1];
+                    oldIds.CopyTo(newIds);
+                    newIds[oldIds.Count] = mediaUrl;
+                    item.RemoteTrailers = newIds;
                 }
             }
         }

+ 9 - 9
MediaBrowser.Controller/Entities/Folder.cs

@@ -666,36 +666,36 @@ namespace MediaBrowser.Controller.Entities
             query.StartIndex = null;
             query.Limit = null;
 
-            var itemsList = LibraryManager.GetItemList(query);
+            IEnumerable<BaseItem> itemsList = LibraryManager.GetItemList(query);
             var user = query.User;
 
             if (user != null)
             {
                 // needed for boxsets
-                itemsList = itemsList.Where(i => i.IsVisibleStandalone(query.User)).ToList();
+                itemsList = itemsList.Where(i => i.IsVisibleStandalone(query.User));
             }
 
-            BaseItem[] returnItems;
+            IEnumerable<BaseItem> returnItems;
             int totalCount = 0;
 
             if (query.EnableTotalRecordCount)
             {
-                var itemsArray = itemsList.ToArray();
-                totalCount = itemsArray.Length;
-                returnItems = itemsArray;
+                var itemArray = itemsList.ToArray();
+                totalCount = itemArray.Length;
+                returnItems = itemArray;
             }
             else
             {
-                returnItems = itemsList.ToArray();
+                returnItems = itemsList;
             }
 
             if (limit.HasValue)
             {
-                returnItems = returnItems.Skip(startIndex ?? 0).Take(limit.Value).ToArray();
+                returnItems = returnItems.Skip(startIndex ?? 0).Take(limit.Value);
             }
             else if (startIndex.HasValue)
             {
-                returnItems = returnItems.Skip(startIndex.Value).ToArray();
+                returnItems = returnItems.Skip(startIndex.Value);
             }
 
             return new QueryResult<BaseItem>

+ 61 - 9
MediaBrowser.Controller/Entities/IHasTrailers.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.Entities
@@ -11,29 +10,82 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the remote trailers.
         /// </summary>
         /// <value>The remote trailers.</value>
-        MediaUrl[] RemoteTrailers { get; set; }
+        IReadOnlyList<MediaUrl> RemoteTrailers { get; set; }
 
         /// <summary>
         /// Gets or sets the local trailer ids.
         /// </summary>
         /// <value>The local trailer ids.</value>
-        Guid[] LocalTrailerIds { get; set; }
-        Guid[] RemoteTrailerIds { get; set; }
+        IReadOnlyList<Guid> LocalTrailerIds { get; set; }
+
+        /// <summary>
+        /// Gets or sets the remote trailer ids.
+        /// </summary>
+        /// <value>The remote trailer ids.</value>
+        IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
+
         Guid Id { get; set; }
     }
 
+    /// <summary>
+    /// Class providing extension methods for working with <see cref="IHasTrailers" />.
+    /// </summary>
     public static class HasTrailerExtensions
     {
+        /// <summary>
+        /// Gets the trailer count.
+        /// </summary>
+        /// <returns><see cref="IReadOnlyList{Guid}" />.</returns>
+        public static int GetTrailerCount(this IHasTrailers item)
+            => item.LocalTrailerIds.Count + item.RemoteTrailerIds.Count;
+
         /// <summary>
         /// Gets the trailer ids.
         /// </summary>
-        /// <returns>List&lt;Guid&gt;.</returns>
-        public static List<Guid> GetTrailerIds(this IHasTrailers item)
+        /// <returns><see cref="IReadOnlyList{Guid}" />.</returns>
+        public static IReadOnlyList<Guid> GetTrailerIds(this IHasTrailers item)
         {
-            var list = item.LocalTrailerIds.ToList();
-            list.AddRange(item.RemoteTrailerIds);
-            return list;
+            var localIds = item.LocalTrailerIds;
+            var remoteIds = item.RemoteTrailerIds;
+
+            var all = new Guid[localIds.Count + remoteIds.Count];
+            var index = 0;
+            foreach (var id in localIds)
+            {
+                all[index++] = id;
+            }
+
+            foreach (var id in remoteIds)
+            {
+                all[index++] = id;
+            }
+
+            return all;
         }
 
+        /// <summary>
+        /// Gets the trailers.
+        /// </summary>
+        /// <returns><see cref="IReadOnlyList{BaseItem}" />.</returns>
+        public static IReadOnlyList<BaseItem> GetTrailers(this IHasTrailers item)
+        {
+            var localIds = item.LocalTrailerIds;
+            var remoteIds = item.RemoteTrailerIds;
+            var libraryManager = BaseItem.LibraryManager;
+
+            var all = new BaseItem[localIds.Count + remoteIds.Count];
+            var index = 0;
+            foreach (var id in localIds)
+            {
+                all[index++] = libraryManager.GetItemById(id);
+            }
+
+            foreach (var id in remoteIds)
+            {
+                all[index++] = libraryManager.GetItemById(id);
+            }
+
+            return all;
+        }
     }
 }

+ 7 - 3
MediaBrowser.Controller/Entities/Movies/BoxSet.cs

@@ -33,8 +33,11 @@ namespace MediaBrowser.Controller.Entities.Movies
         [IgnoreDataMember]
         public override bool SupportsPeople => true;
 
-        public Guid[] LocalTrailerIds { get; set; }
-        public Guid[] RemoteTrailerIds { get; set; }
+        /// <inheritdoc />
+        public IReadOnlyList<Guid> LocalTrailerIds { get; set; }
+
+        /// <inheritdoc />
+        public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
 
         /// <summary>
         /// Gets or sets the display order.
@@ -61,7 +64,8 @@ namespace MediaBrowser.Controller.Entities.Movies
             {
                 return base.GetNonCachedChildren(directoryService);
             }
-            return new List<BaseItem>();
+
+            return Enumerable.Empty<BaseItem>();
         }
 
         protected override List<BaseItem> LoadChildren()

+ 5 - 2
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -27,8 +27,11 @@ namespace MediaBrowser.Controller.Entities.Movies
             RemoteTrailerIds = Array.Empty<Guid>();
         }
 
-        public Guid[] LocalTrailerIds { get; set; }
-        public Guid[] RemoteTrailerIds { get; set; }
+        /// <inheritdoc />
+        public IReadOnlyList<Guid> LocalTrailerIds { get; set; }
+
+        /// <inheritdoc />
+        public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
 
         /// <summary>
         /// Gets or sets the name of the TMDB collection.

+ 5 - 2
MediaBrowser.Controller/Entities/TV/Episode.cs

@@ -23,8 +23,11 @@ namespace MediaBrowser.Controller.Entities.TV
             RemoteTrailerIds = Array.Empty<Guid>();
         }
 
-        public Guid[] LocalTrailerIds { get; set; }
-        public Guid[] RemoteTrailerIds { get; set; }
+        /// <inheritdoc />
+        public IReadOnlyList<Guid> LocalTrailerIds { get; set; }
+
+        /// <inheritdoc />
+        public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
 
         /// <summary>
         /// Gets the season in which it aired.

+ 5 - 2
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -46,8 +46,11 @@ namespace MediaBrowser.Controller.Entities.TV
         [IgnoreDataMember]
         public override bool SupportsPeople => true;
 
-        public Guid[] LocalTrailerIds { get; set; }
-        public Guid[] RemoteTrailerIds { get; set; }
+        /// <inheritdoc />
+        public IReadOnlyList<Guid> LocalTrailerIds { get; set; }
+
+        /// <inheritdoc />
+        public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
 
         /// <summary>
         /// airdate, dvd or absolute

+ 2 - 2
MediaBrowser.Controller/LiveTv/ILiveTvManager.cs

@@ -221,7 +221,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="fields">The fields.</param>
         /// <param name="user">The user.</param>
         /// <returns>Task.</returns>
-        Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> programs, ItemFields[] fields, User user = null);
+        Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> programs, ItemFields[] fields, User user = null);
 
         /// <summary>
         /// Saves the tuner host.
@@ -258,7 +258,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="items">The items.</param>
         /// <param name="options">The options.</param>
         /// <param name="user">The user.</param>
-        void AddChannelInfo(List<Tuple<BaseItemDto, LiveTvChannel>> items, DtoOptions options, User user);
+        void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> items, DtoOptions options, User user);
 
         Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken);
         Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken);

+ 1 - 1
MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs

@@ -228,7 +228,7 @@ namespace MediaBrowser.LocalMetadata.Savers
                 }
             }
 
-            if (item.RemoteTrailers.Length > 0)
+            if (item.RemoteTrailers.Count > 0)
             {
                 writer.WriteStartElement("Trailers");
 

+ 1 - 1
MediaBrowser.Model/Dto/BaseItemDto.cs

@@ -234,7 +234,7 @@ namespace MediaBrowser.Model.Dto
         /// Gets or sets the trailer urls.
         /// </summary>
         /// <value>The trailer urls.</value>
-        public MediaUrl[] RemoteTrailers { get; set; }
+        public IReadOnlyCollection<MediaUrl> RemoteTrailers { get; set; }
 
         /// <summary>
         /// Gets or sets the provider ids.

+ 2 - 1
MediaBrowser.Model/Dto/RecommendationDto.cs

@@ -1,10 +1,11 @@
 using System;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Model.Dto
 {
     public class RecommendationDto
     {
-        public BaseItemDto[] Items { get; set; }
+        public IReadOnlyCollection<BaseItemDto> Items { get; set; }
 
         public RecommendationType RecommendationType { get; set; }
 

+ 5 - 2
MediaBrowser.Model/Querying/QueryResult.cs

@@ -1,3 +1,6 @@
+using System;
+using System.Collections.Generic;
+
 namespace MediaBrowser.Model.Querying
 {
     public class QueryResult<T>
@@ -6,7 +9,7 @@ namespace MediaBrowser.Model.Querying
         /// Gets or sets the items.
         /// </summary>
         /// <value>The items.</value>
-        public T[] Items { get; set; }
+        public IReadOnlyList<T> Items { get; set; }
 
         /// <summary>
         /// The total number of records available
@@ -16,7 +19,7 @@ namespace MediaBrowser.Model.Querying
 
         public QueryResult()
         {
-            Items = new T[] { };
+            Items = Array.Empty<T>();
         }
     }
 }

+ 17 - 21
MediaBrowser.Providers/Manager/ProviderUtils.cs

@@ -11,7 +11,8 @@ namespace MediaBrowser.Providers.Manager
 {
     public static class ProviderUtils
     {
-        public static void MergeBaseItemData<T>(MetadataResult<T> sourceResult,
+        public static void MergeBaseItemData<T>(
+            MetadataResult<T> sourceResult,
             MetadataResult<T> targetResult,
             MetadataFields[] lockedFields,
             bool replaceData,
@@ -174,11 +175,11 @@ namespace MediaBrowser.Providers.Manager
                 }
             }
 
-            MergeAlbumArtist(source, target, lockedFields, replaceData);
-            MergeCriticRating(source, target, lockedFields, replaceData);
-            MergeTrailers(source, target, lockedFields, replaceData);
-            MergeVideoInfo(source, target, lockedFields, replaceData);
-            MergeDisplayOrder(source, target, lockedFields, replaceData);
+            MergeAlbumArtist(source, target, replaceData);
+            MergeCriticRating(source, target, replaceData);
+            MergeTrailers(source, target, replaceData);
+            MergeVideoInfo(source, target, replaceData);
+            MergeDisplayOrder(source, target, replaceData);
 
             if (replaceData || string.IsNullOrEmpty(target.ForcedSortName))
             {
@@ -196,7 +197,7 @@ namespace MediaBrowser.Providers.Manager
                 target.IsLocked = source.IsLocked;
 
                 // Grab the value if it's there, but if not then don't overwrite the default
-                if (source.DateCreated != default(DateTime))
+                if (source.DateCreated != default)
                 {
                     target.DateCreated = source.DateCreated;
                 }
@@ -231,12 +232,10 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        private static void MergeDisplayOrder(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData)
+        private static void MergeDisplayOrder(BaseItem source, BaseItem target, bool replaceData)
         {
-            var sourceHasDisplayOrder = source as IHasDisplayOrder;
-            var targetHasDisplayOrder = target as IHasDisplayOrder;
-
-            if (sourceHasDisplayOrder != null && targetHasDisplayOrder != null)
+            if (source is IHasDisplayOrder sourceHasDisplayOrder
+                && target is IHasDisplayOrder targetHasDisplayOrder)
             {
                 if (replaceData || string.IsNullOrEmpty(targetHasDisplayOrder.DisplayOrder))
                 {
@@ -250,7 +249,7 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        private static void MergeAlbumArtist(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData)
+        private static void MergeAlbumArtist(BaseItem source, BaseItem target, bool replaceData)
         {
             if (source is IHasAlbumArtist sourceHasAlbumArtist
                 && target is IHasAlbumArtist targetHasAlbumArtist)
@@ -262,7 +261,7 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        private static void MergeCriticRating(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData)
+        private static void MergeCriticRating(BaseItem source, BaseItem target, bool replaceData)
         {
             if (replaceData || !target.CriticRating.HasValue)
             {
@@ -270,20 +269,17 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        private static void MergeTrailers(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData)
+        private static void MergeTrailers(BaseItem source, BaseItem target, bool replaceData)
         {
-            if (replaceData || target.RemoteTrailers.Length == 0)
+            if (replaceData || target.RemoteTrailers.Count == 0)
             {
                 target.RemoteTrailers = source.RemoteTrailers;
             }
         }
 
-        private static void MergeVideoInfo(BaseItem source, BaseItem target, MetadataFields[] lockedFields, bool replaceData)
+        private static void MergeVideoInfo(BaseItem source, BaseItem target, bool replaceData)
         {
-            var sourceCast = source as Video;
-            var targetCast = target as Video;
-
-            if (sourceCast != null && targetCast != null)
+            if (source is Video sourceCast && target is Video targetCast)
             {
                 if (replaceData || targetCast.Video3DFormat == null)
                 {

+ 1 - 1
MediaBrowser.Providers/MediaBrowser.Providers.csproj

@@ -28,5 +28,5 @@
     <!-- We need at least C# 7.1 -->
     <LangVersion>latest</LangVersion>
   </PropertyGroup>
-
+   
 </Project>