From 827602711e54bb966da0daa1e265e590a15a787d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 20 Nov 2016 15:20:01 -0500 Subject: [PATCH] update sqlite dependencies --- Emby.Dlna/ContentDirectory/ControlHandler.cs | 2 +- .../Data/SqliteItemRepository.cs | 462 ++++++++++-------- .../OrganizerScheduledTask.cs | 2 +- MediaBrowser.Controller/Entities/Person.cs | 4 +- .../People/TvdbPersonImageProvider.cs | 5 +- .../Emby.Server.Mac.csproj | 6 - .../MediaBrowser.Server.Mono.csproj | 11 +- .../SQLitePCLRaw.provider.sqlite3.dll.config | 3 - MediaBrowser.Server.Mono/packages.config | 1 - MediaBrowser.XbmcMetadata/EntryPoint.cs | 2 +- 10 files changed, 259 insertions(+), 239 deletions(-) delete mode 100644 MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 8cb59b0569..d77919e47a 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -509,7 +509,7 @@ namespace Emby.Dlna.ContentDirectory { var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user) { - Person = person.Name, + PersonIds = new[] { person.Id.ToString("N") }, IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(Trailer).Name }, SortBy = new[] { ItemSortBy.SortName }, Limit = limit, diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index a3c1ee6906..e4ceca2679 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3434,8 +3434,23 @@ namespace Emby.Server.Implementations.Data if (query.PersonIds.Length > 0) { - // Todo: improve without having to do this - query.Person = query.PersonIds.Select(i => RetrieveItem(new Guid(i))).Where(i => i != null).Select(i => i.Name).FirstOrDefault(); + // TODO: Should this query with CleanName ? + + var clauses = new List(); + var index = 0; + foreach (var personId in query.PersonIds) + { + var paramName = "@PersonId" + index; + + clauses.Add("(select Name from TypedBaseItems where guid=" + paramName + ") in (select Name from People where ItemId=Guid)"); + if (statement != null) + { + statement.TryBind(paramName, personId.ToGuidParamValue()); + } + index++; + } + var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")"; + whereClauses.Add(clause); } if (!string.IsNullOrWhiteSpace(query.Person)) @@ -3633,25 +3648,36 @@ namespace Emby.Server.Implementations.Data var index = 0; foreach (var artistId in query.ExcludeArtistIds) { - var artistItem = RetrieveItem(new Guid(artistId)); - if (artistItem != null) + var paramName = "@ExcludeArtistId" + index; + + clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from itemvalues where ItemId=Guid and Type<=1)"); + if (statement != null) { - clauses.Add("@ExcludeArtistName" + index + " not in (select CleanValue from itemvalues where ItemId=Guid and Type <= 1)"); - if (statement != null) - { - statement.TryBind("@ExcludeArtistName" + index, GetCleanValue(artistItem.Name)); - } - index++; + statement.TryBind(paramName, artistId.ToGuidParamValue()); } + index++; } - var clause = "(" + string.Join(" AND ", clauses.ToArray()) + ")"; + var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")"; whereClauses.Add(clause); } if (query.GenreIds.Length > 0) { - // Todo: improve without having to do this - query.Genres = query.GenreIds.Select(i => RetrieveItem(new Guid(i))).Where(i => i != null).Select(i => i.Name).ToArray(); + var clauses = new List(); + var index = 0; + foreach (var genreId in query.GenreIds) + { + var paramName = "@GenreId" + index; + + clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=2)"); + if (statement != null) + { + statement.TryBind(paramName, genreId.ToGuidParamValue()); + } + index++; + } + var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")"; + whereClauses.Add(clause); } if (query.Genres.Length > 0) @@ -3690,8 +3716,21 @@ namespace Emby.Server.Implementations.Data if (query.StudioIds.Length > 0) { - // Todo: improve without having to do this - query.Studios = query.StudioIds.Select(i => RetrieveItem(new Guid(i))).Where(i => i != null).Select(i => i.Name).ToArray(); + var clauses = new List(); + var index = 0; + foreach (var studioId in query.StudioIds) + { + var paramName = "@StudioId" + index; + + clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=3)"); + if (statement != null) + { + statement.TryBind(paramName, studioId.ToGuidParamValue()); + } + index++; + } + var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")"; + whereClauses.Add(clause); } if (query.Studios.Length > 0) @@ -4655,205 +4694,204 @@ namespace Emby.Server.Implementations.Data ("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) : ("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray()) + ")"); + InternalItemsQuery typeSubQuery = null; + + var itemCountColumns = new List>(); + + var typesToCount = query.IncludeItemTypes.ToList(); + + if (typesToCount.Count > 0) + { + var itemCountColumnQuery = "select group_concat(type, '|')" + GetFromText("B"); + + typeSubQuery = new InternalItemsQuery(query.User) + { + ExcludeItemTypes = query.ExcludeItemTypes, + IncludeItemTypes = query.IncludeItemTypes, + MediaTypes = query.MediaTypes, + AncestorIds = query.AncestorIds, + ExcludeItemIds = query.ExcludeItemIds, + ItemIds = query.ItemIds, + TopParentIds = query.TopParentIds, + ParentId = query.ParentId, + IsPlayed = query.IsPlayed + }; + var whereClauses = GetWhereClauses(typeSubQuery, null, "itemTypes"); + + whereClauses.Add("guid in (select ItemId from ItemValues where ItemValues.CleanValue=A.CleanName AND " + typeClause + ")"); + + var typeWhereText = whereClauses.Count == 0 ? + string.Empty : + " where " + string.Join(" AND ", whereClauses.ToArray()); + + itemCountColumnQuery += typeWhereText; + + //itemCountColumnQuery += ")"; + + itemCountColumns.Add(new Tuple("itemTypes", "(" + itemCountColumnQuery + ") as itemTypes")); + } + + var columns = _retriveItemColumns.ToList(); + columns.AddRange(itemCountColumns.Select(i => i.Item2).ToArray()); + + var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, columns.ToArray())) + GetFromText(); + commandText += GetJoinUserDataText(query); + + var innerQuery = new InternalItemsQuery(query.User) + { + ExcludeItemTypes = query.ExcludeItemTypes, + IncludeItemTypes = query.IncludeItemTypes, + MediaTypes = query.MediaTypes, + AncestorIds = query.AncestorIds, + ExcludeItemIds = query.ExcludeItemIds, + ItemIds = query.ItemIds, + TopParentIds = query.TopParentIds, + ParentId = query.ParentId, + IsPlayed = query.IsPlayed + }; + + var innerWhereClauses = GetWhereClauses(innerQuery, null); + + var innerWhereText = innerWhereClauses.Count == 0 ? + string.Empty : + " where " + string.Join(" AND ", innerWhereClauses.ToArray()); + + var whereText = " where Type=@SelectType"; + + if (typesToCount.Count == 0) + { + whereText += " And CleanName In (Select CleanValue from ItemValues where " + typeClause + " AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))"; + } + else + { + //whereText += " And itemTypes not null"; + whereText += " And CleanName In (Select CleanValue from ItemValues where " + typeClause + " AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))"; + } + + var outerQuery = new InternalItemsQuery(query.User) + { + IsFavorite = query.IsFavorite, + IsFavoriteOrLiked = query.IsFavoriteOrLiked, + IsLiked = query.IsLiked, + IsLocked = query.IsLocked, + NameLessThan = query.NameLessThan, + NameStartsWith = query.NameStartsWith, + NameStartsWithOrGreater = query.NameStartsWithOrGreater, + AlbumArtistStartsWithOrGreater = query.AlbumArtistStartsWithOrGreater, + Tags = query.Tags, + OfficialRatings = query.OfficialRatings, + GenreIds = query.GenreIds, + Genres = query.Genres, + Years = query.Years + }; + + var outerWhereClauses = GetWhereClauses(outerQuery, null); + + whereText += outerWhereClauses.Count == 0 ? + string.Empty : + " AND " + string.Join(" AND ", outerWhereClauses.ToArray()); + //cmd.CommandText += GetGroupBy(query); + + commandText += whereText; + commandText += " group by PresentationUniqueKey"; + + commandText += " order by SortName"; + + if (query.Limit.HasValue || query.StartIndex.HasValue) + { + var offset = query.StartIndex ?? 0; + + if (query.Limit.HasValue || offset > 0) + { + commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture); + } + + if (offset > 0) + { + commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture); + } + } + + var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0; + + var list = new List>(); + var count = 0; + using (WriteLock.Write()) { - // var itemCountColumns = new List>(); - - // var typesToCount = query.IncludeItemTypes.ToList(); - - // if (typesToCount.Count > 0) - // { - // var itemCountColumnQuery = "select group_concat(type, '|')" + GetFromText("B"); - - // var typeSubQuery = new InternalItemsQuery(query.User) - // { - // ExcludeItemTypes = query.ExcludeItemTypes, - // IncludeItemTypes = query.IncludeItemTypes, - // MediaTypes = query.MediaTypes, - // AncestorIds = query.AncestorIds, - // ExcludeItemIds = query.ExcludeItemIds, - // ItemIds = query.ItemIds, - // TopParentIds = query.TopParentIds, - // ParentId = query.ParentId, - // IsPlayed = query.IsPlayed - // }; - // var whereClauses = GetWhereClauses(typeSubQuery, cmd, "itemTypes"); - - // whereClauses.Add("guid in (select ItemId from ItemValues where ItemValues.CleanValue=A.CleanName AND " + typeClause + ")"); - - // var typeWhereText = whereClauses.Count == 0 ? - // string.Empty : - // " where " + string.Join(" AND ", whereClauses.ToArray()); - - // itemCountColumnQuery += typeWhereText; - - // //itemCountColumnQuery += ")"; - - // itemCountColumns.Add(new Tuple("itemTypes", "(" + itemCountColumnQuery + ") as itemTypes")); - // } - - // var columns = _retriveItemColumns.ToList(); - // columns.AddRange(itemCountColumns.Select(i => i.Item2).ToArray()); - - // cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, columns.ToArray(), cmd)) + GetFromText(); - // cmd.CommandText += GetJoinUserDataText(query); - - // var innerQuery = new InternalItemsQuery(query.User) - // { - // ExcludeItemTypes = query.ExcludeItemTypes, - // IncludeItemTypes = query.IncludeItemTypes, - // MediaTypes = query.MediaTypes, - // AncestorIds = query.AncestorIds, - // ExcludeItemIds = query.ExcludeItemIds, - // ItemIds = query.ItemIds, - // TopParentIds = query.TopParentIds, - // ParentId = query.ParentId, - // IsPlayed = query.IsPlayed - // }; - - // var innerWhereClauses = GetWhereClauses(innerQuery, cmd); - - // var innerWhereText = innerWhereClauses.Count == 0 ? - // string.Empty : - // " where " + string.Join(" AND ", innerWhereClauses.ToArray()); - - // var whereText = " where Type=@SelectType"; - - // if (typesToCount.Count == 0) - // { - // whereText += " And CleanName In (Select CleanValue from ItemValues where " + typeClause + " AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))"; - // } - // else - // { - // //whereText += " And itemTypes not null"; - // whereText += " And CleanName In (Select CleanValue from ItemValues where " + typeClause + " AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))"; - // } - - // var outerQuery = new InternalItemsQuery(query.User) - // { - // IsFavorite = query.IsFavorite, - // IsFavoriteOrLiked = query.IsFavoriteOrLiked, - // IsLiked = query.IsLiked, - // IsLocked = query.IsLocked, - // NameLessThan = query.NameLessThan, - // NameStartsWith = query.NameStartsWith, - // NameStartsWithOrGreater = query.NameStartsWithOrGreater, - // AlbumArtistStartsWithOrGreater = query.AlbumArtistStartsWithOrGreater, - // Tags = query.Tags, - // OfficialRatings = query.OfficialRatings, - // GenreIds = query.GenreIds, - // Genres = query.Genres, - // Years = query.Years - // }; - - // var outerWhereClauses = GetWhereClauses(outerQuery, cmd); - - // whereText += outerWhereClauses.Count == 0 ? - // string.Empty : - // " AND " + string.Join(" AND ", outerWhereClauses.ToArray()); - // //cmd.CommandText += GetGroupBy(query); - - // cmd.CommandText += whereText; - // cmd.CommandText += " group by PresentationUniqueKey"; - - // cmd.Parameters.Add(cmd, "@SelectType", DbType.String).Value = returnType; - - // if (EnableJoinUserData(query)) - // { - // cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = query.User.Id; - // } - - // cmd.CommandText += " order by SortName"; - - // if (query.Limit.HasValue || query.StartIndex.HasValue) - // { - // var offset = query.StartIndex ?? 0; - - // if (query.Limit.HasValue || offset > 0) - // { - // cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture); - // } - - // if (offset > 0) - // { - // cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture); - // } - // } - - // cmd.CommandText += ";"; - - // var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0; - - // if (isReturningZeroItems) - // { - // cmd.CommandText = ""; - // } - - // if (query.EnableTotalRecordCount) - // { - // cmd.CommandText += "select count (distinct PresentationUniqueKey)" + GetFromText(); - - // cmd.CommandText += GetJoinUserDataText(query); - // cmd.CommandText += whereText; - // } - // else - // { - // cmd.CommandText = cmd.CommandText.TrimEnd(';'); - // } - - // var list = new List>(); - // var count = 0; - - // var commandBehavior = isReturningZeroItems || !query.EnableTotalRecordCount - // ? (CommandBehavior.SequentialAccess | CommandBehavior.SingleResult) - // : CommandBehavior.SequentialAccess; - - // //Logger.Debug("GetItemValues: " + cmd.CommandText); - - // using (var reader = cmd.ExecuteReader(commandBehavior)) - // { - // LogQueryTime("GetItemValues", cmd, now); - - // if (isReturningZeroItems) - // { - // if (reader.Read()) - // { - // count = reader.GetInt32(0); - // } - // } - // else - // { - // while (reader.Read()) - // { - // var item = GetItem(reader); - // if (item != null) - // { - // var countStartColumn = columns.Count - 1; - - // list.Add(new Tuple(item, GetItemCounts(reader, countStartColumn, typesToCount))); - // } - // } - - // if (reader.NextResult() && reader.Read()) - // { - // count = reader.GetInt32(0); - // } - // } - // } - - // if (count == 0) - // { - // count = list.Count; - // } - - // return new QueryResult> - // { - // Items = list.ToArray(), - // TotalRecordCount = count - // }; - } - - return new QueryResult>(); + if (!isReturningZeroItems) + { + using (var statement = _connection.PrepareStatement(commandText)) + { + statement.TryBind("@SelectType", returnType); + if (EnableJoinUserData(query)) + { + statement.TryBind("@UserId", query.User.Id); + } + + if (typeSubQuery != null) + { + GetWhereClauses(typeSubQuery, null, "itemTypes"); + } + BindSimilarParams(query, statement); + GetWhereClauses(innerQuery, statement); + GetWhereClauses(outerQuery, statement); + + foreach (var row in statement.ExecuteQuery()) + { + var item = GetItem(row); + if (item != null) + { + var countStartColumn = columns.Count - 1; + + list.Add(new Tuple(item, GetItemCounts(row, countStartColumn, typesToCount))); + } + } + + LogQueryTime("GetItemValues", commandText, now); + } + } + + if (query.EnableTotalRecordCount) + { + commandText = "select count (distinct PresentationUniqueKey)" + GetFromText(); + + commandText += GetJoinUserDataText(query); + commandText += whereText; + + using (var statement = _connection.PrepareStatement(commandText)) + { + statement.TryBind("@SelectType", returnType); + if (EnableJoinUserData(query)) + { + statement.TryBind("@UserId", query.User.Id); + } + + if (typeSubQuery != null) + { + GetWhereClauses(typeSubQuery, null, "itemTypes"); + } + BindSimilarParams(query, statement); + GetWhereClauses(innerQuery, statement); + GetWhereClauses(outerQuery, statement); + + count = statement.ExecuteQuery().SelectScalarInt().First(); + + LogQueryTime("GetItemValues", commandText, now); + } + } + } + + if (count == 0) + { + count = list.Count; + } + + return new QueryResult> + { + Items = list.ToArray(), + TotalRecordCount = count + }; } private ItemCounts GetItemCounts(IReadOnlyList reader, int countStartColumn, List typesToCount) diff --git a/Emby.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs b/Emby.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs index 48a85e0e0e..5be7ba7ada 100644 --- a/Emby.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs +++ b/Emby.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs @@ -74,7 +74,7 @@ namespace Emby.Server.Implementations.FileOrganization return new[] { // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromMinutes(15).Ticks} + new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromMinutes(5).Ticks} }; } diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index 79d2a2ee5d..0c36442af3 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Controller.Entities public IEnumerable GetTaggedItems(InternalItemsQuery query) { - query.Person = Name; + query.PersonIds = new[] { Id.ToString("N") }; return LibraryManager.GetItemList(query); } @@ -95,7 +95,7 @@ namespace MediaBrowser.Controller.Entities { var itemsWithPerson = LibraryManager.GetItemIds(new InternalItemsQuery { - Person = Name + PersonIds = new[] { Id.ToString("N") } }); return inputItems.Where(i => itemsWithPerson.Contains(i.Id)); diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index 817b9d71af..0384ddd253 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -62,13 +62,10 @@ namespace MediaBrowser.Providers.People public Task> GetImages(IHasImages item, CancellationToken cancellationToken) { - // Avoid implicitly captured closure - var itemName = item.Name; - var seriesWithPerson = _libraryManager.GetItemList(new InternalItemsQuery { IncludeItemTypes = new[] { typeof(Series).Name }, - Person = itemName + PersonIds = new[] { item.Id.ToString("N") } }).Cast() .Where(i => TvdbSeriesProvider.IsValidSeries(i.ProviderIds)) diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj index f80513c51f..9df2df2ff4 100644 --- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj +++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj @@ -115,9 +115,6 @@ ..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.1-pre20161109081005\lib\net45\SQLitePCLRaw.provider.sqlite3.dll True - - ..\ThirdParty\System.Data.SQLite.ManagedOnly\1.0.94.0\System.Data.SQLite.dll - ..\ThirdParty\emby\Emby.Common.Implementations.dll @@ -151,9 +148,6 @@ - - Native\SqliteExtensions.cs - diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj index 13d67ace5a..485bbf3a97 100644 --- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj +++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj @@ -88,16 +88,15 @@ ..\packages\SQLitePCLRaw.core.1.1.1-pre20161109081005\lib\net45\SQLitePCLRaw.core.dll True - - ..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.1-pre20161109081005\lib\net45\SQLitePCLRaw.provider.sqlite3.dll - True - ..\ThirdParty\MediaBrowser.IsoMounting.Linux\MediaBrowser.IsoMounting.Linux.dll + + ..\ThirdParty\SQLitePCLRaw.provider.sqlite3.net45\System.Data.SQLite.dll + @@ -223,10 +222,6 @@ PreserveNewest - - Designer - PreserveNewest - PreserveNewest Designer diff --git a/MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config b/MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config deleted file mode 100644 index 83a6cd9f38..0000000000 --- a/MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/MediaBrowser.Server.Mono/packages.config b/MediaBrowser.Server.Mono/packages.config index e82ef0ed04..6e3fe428c1 100644 --- a/MediaBrowser.Server.Mono/packages.config +++ b/MediaBrowser.Server.Mono/packages.config @@ -6,5 +6,4 @@ - \ No newline at end of file diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs index bf3d3c3039..cd18e56706 100644 --- a/MediaBrowser.XbmcMetadata/EntryPoint.cs +++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs @@ -52,7 +52,7 @@ namespace MediaBrowser.XbmcMetadata var items = _libraryManager.GetItemList(new InternalItemsQuery { - Person = person.Name + PersonIds = new [] { person.Id.ToString("N") } }).ToList();