Some memory management improvements

pull/1872/head
tidusjar 7 years ago
parent 059b083359
commit 6ac9da8bed

@ -26,8 +26,7 @@ namespace Ombi.Api.Plex
/// This is for authenticating users credentials with Plex /// This is for authenticating users credentials with Plex
/// <para>NOTE: Plex "Managed" users do not work</para> /// <para>NOTE: Plex "Managed" users do not work</para>
/// </summary> /// </summary>
/// <param name="username"></param> /// <param name="user"></param>
/// <param name="password"></param>
/// <returns></returns> /// <returns></returns>
public async Task<PlexAuthentication> SignIn(UserRequest user) public async Task<PlexAuthentication> SignIn(UserRequest user)
{ {

@ -1,32 +1,23 @@
using System; using System.IO;
using System.IO;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Security.Authentication;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Serialization; using System.Xml.Serialization;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json; using Newtonsoft.Json;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Ombi.Core.Settings;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Settings.Settings.Models;
namespace Ombi.Api namespace Ombi.Api
{ {
public class Api : IApi public class Api : IApi
{ {
public Api(ILogger<Api> log, ISettingsService<OmbiSettings> s, ICacheService cache, IOmbiHttpClient client) public Api(ILogger<Api> log, IOmbiHttpClient client)
{ {
Logger = log; Logger = log;
_settings = s;
_cache = cache;
_client = client; _client = client;
} }
private ILogger<Api> Logger { get; } private ILogger<Api> Logger { get; }
private readonly ISettingsService<OmbiSettings> _settings;
private readonly ICacheService _cache;
private readonly IOmbiHttpClient _client; private readonly IOmbiHttpClient _client;
private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
@ -38,41 +29,29 @@ namespace Ombi.Api
{ {
using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri)) using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri))
{ {
// Add the Json Body AddHeadersBody(request, httpRequestMessage);
if (request.JsonBody != null)
var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
if (!httpResponseMessage.IsSuccessStatusCode)
{ {
httpRequestMessage.Content = new JsonContent(request.JsonBody); LogError(request, httpResponseMessage);
httpRequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); // Emby connect fails if we have the charset in the header
} }
// Add headers // do something with the response
foreach (var header in request.Headers) var receivedString = await httpResponseMessage.Content.ReadAsStringAsync();
if (request.ContentType == ContentType.Json)
{ {
httpRequestMessage.Headers.Add(header.Key, header.Value); request.OnBeforeDeserialization?.Invoke(receivedString);
return JsonConvert.DeserializeObject<T>(receivedString, Settings);
} }
using (var httpResponseMessage = await _client.SendAsync(httpRequestMessage)) else
{ {
if (!httpResponseMessage.IsSuccessStatusCode) // XML
{ XmlSerializer serializer = new XmlSerializer(typeof(T));
LogError(request, httpResponseMessage); StringReader reader = new StringReader(receivedString);
} var value = (T)serializer.Deserialize(reader);
// do something with the response return value;
var data = httpResponseMessage.Content;
var receivedString = await data.ReadAsStringAsync();
if (request.ContentType == ContentType.Json)
{
request.OnBeforeDeserialization?.Invoke(receivedString);
return JsonConvert.DeserializeObject<T>(receivedString, Settings);
}
else
{
// XML
XmlSerializer serializer = new XmlSerializer(typeof(T));
StringReader reader = new StringReader(receivedString);
var value = (T)serializer.Deserialize(reader);
return value;
}
} }
} }
@ -82,30 +61,17 @@ namespace Ombi.Api
{ {
using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri)) using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri))
{ {
// Add the Json Body AddHeadersBody(request, httpRequestMessage);
if (request.JsonBody != null)
{
httpRequestMessage.Content = new JsonContent(request.JsonBody);
}
// Add headers var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
foreach (var header in request.Headers) if (!httpResponseMessage.IsSuccessStatusCode)
{ {
httpRequestMessage.Headers.Add(header.Key, header.Value); LogError(request, httpResponseMessage);
} }
using (var httpResponseMessage = await _client.SendAsync(httpRequestMessage)) // do something with the response
{ var data = httpResponseMessage.Content;
if (!httpResponseMessage.IsSuccessStatusCode)
{
LogError(request, httpResponseMessage);
}
// do something with the response
var data = httpResponseMessage.Content;
return await data.ReadAsStringAsync(); return await data.ReadAsStringAsync();
}
} }
} }
@ -114,25 +80,29 @@ namespace Ombi.Api
{ {
using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri)) using (var httpRequestMessage = new HttpRequestMessage(request.HttpMethod, request.FullUri))
{ {
// Add the Json Body AddHeadersBody(request, httpRequestMessage);
if (request.JsonBody != null) var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
if (!httpResponseMessage.IsSuccessStatusCode)
{ {
httpRequestMessage.Content = new JsonContent(request.JsonBody); LogError(request, httpResponseMessage);
} }
}
}
// Add headers private static void AddHeadersBody(Request request, HttpRequestMessage httpRequestMessage)
foreach (var header in request.Headers) {
{ // Add the Json Body
httpRequestMessage.Headers.Add(header.Key, header.Value); if (request.JsonBody != null)
{
httpRequestMessage.Content = new JsonContent(request.JsonBody);
httpRequestMessage.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/json"); // Emby connect fails if we have the charset in the header
}
} // Add headers
using (var httpResponseMessage = await _client.SendAsync(httpRequestMessage)) foreach (var header in request.Headers)
{ {
if (!httpResponseMessage.IsSuccessStatusCode) httpRequestMessage.Headers.Add(header.Key, header.Value);
{
LogError(request, httpResponseMessage);
}
}
} }
} }

@ -50,6 +50,7 @@ using Ombi.Store.Repository.Requests;
using Ombi.Updater; using Ombi.Updater;
using PlexContentCacher = Ombi.Schedule.Jobs.Plex; using PlexContentCacher = Ombi.Schedule.Jobs.Plex;
using Ombi.Api.Telegram; using Ombi.Api.Telegram;
using Ombi.Schedule.Jobs.Plex.Interfaces;
using Ombi.Schedule.Jobs.SickRage; using Ombi.Schedule.Jobs.SickRage;
namespace Ombi.DependencyInjection namespace Ombi.DependencyInjection
@ -85,7 +86,7 @@ namespace Ombi.DependencyInjection
public static void RegisterApi(this IServiceCollection services) public static void RegisterApi(this IServiceCollection services)
{ {
services.AddTransient<IApi, Api.Api>(); services.AddSingleton<IApi, Api.Api>();
services.AddSingleton<IOmbiHttpClient, OmbiHttpClient>(); // https://blogs.msdn.microsoft.com/alazarev/2017/12/29/disposable-finalizers-and-httpclient/ services.AddSingleton<IOmbiHttpClient, OmbiHttpClient>(); // https://blogs.msdn.microsoft.com/alazarev/2017/12/29/disposable-finalizers-and-httpclient/
services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>(); services.AddTransient<IMovieDbApi, Api.TheMovieDb.TheMovieDbApi>();
services.AddTransient<IPlexApi, PlexApi>(); services.AddTransient<IPlexApi, PlexApi>();

@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Hangfire; using Hangfire;
using Microsoft.Extensions.DependencyInjection;
namespace Ombi.Schedule namespace Ombi.Schedule
{ {
@ -15,8 +16,13 @@ namespace Ombi.Schedule
public override object ActivateJob(Type type) public override object ActivateJob(Type type)
{ {
var i = type.GetTypeInfo().ImplementedInterfaces.FirstOrDefault(); var scopeFactory = _container.GetService<IServiceScopeFactory>();
return _container.GetService(i); var scope = scopeFactory.CreateScope();
var scopedContainer = scope.ServiceProvider;
var interfaceType = type.GetTypeInfo().ImplementedInterfaces.FirstOrDefault();
var implementation = scopedContainer.GetRequiredService(interfaceType);
return implementation;
} }
} }
} }

@ -148,5 +148,24 @@ namespace Ombi.Schedule.Jobs.Emby
await _tvRepo.Save(); await _tvRepo.Save();
} }
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_movieRepo?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

@ -139,6 +139,26 @@ namespace Ombi.Schedule.Jobs.Emby
return true; return true;
} }
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_settings?.Dispose();
_repo?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

@ -121,5 +121,26 @@ namespace Ombi.Schedule.Jobs.Emby
await _repo.AddRange(epToAdd); await _repo.AddRange(epToAdd);
} }
} }
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_settings?.Dispose();
_repo?.Dispose();
_avaliabilityChecker?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
@ -142,5 +143,26 @@ namespace Ombi.Schedule.Jobs.Emby
} }
} }
} }
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_userManager?.Dispose();
_embySettings?.Dispose();
_userManagementSettings?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

@ -2,7 +2,7 @@
namespace Ombi.Schedule.Jobs.Emby namespace Ombi.Schedule.Jobs.Emby
{ {
public interface IEmbyAvaliabilityChecker public interface IEmbyAvaliabilityChecker : IBaseJob
{ {
Task Start(); Task Start();
} }

@ -2,7 +2,7 @@ using System.Threading.Tasks;
namespace Ombi.Schedule.Jobs.Emby namespace Ombi.Schedule.Jobs.Emby
{ {
public interface IEmbyContentSync public interface IEmbyContentSync : IBaseJob
{ {
Task Start(); Task Start();
} }

@ -2,7 +2,7 @@
namespace Ombi.Schedule.Jobs.Emby namespace Ombi.Schedule.Jobs.Emby
{ {
public interface IEmbyEpisodeSync public interface IEmbyEpisodeSync : IBaseJob
{ {
Task Start(); Task Start();
} }

@ -2,7 +2,7 @@
namespace Ombi.Schedule.Jobs.Emby namespace Ombi.Schedule.Jobs.Emby
{ {
public interface IEmbyUserImporter public interface IEmbyUserImporter : IBaseJob
{ {
Task Start(); Task Start();
} }

@ -0,0 +1,36 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2018 Jamie Rees
// File: IBaseJob.cs
// Created By: Jamie Rees
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/
#endregion
using System;
namespace Ombi.Schedule
{
public interface IBaseJob : IDisposable
{
}
}

@ -2,7 +2,7 @@
namespace Ombi.Schedule.Jobs.Plex namespace Ombi.Schedule.Jobs.Plex
{ {
public interface IPlexAvailabilityChecker public interface IPlexAvailabilityChecker : IBaseJob
{ {
Task Start(); Task Start();
} }

@ -2,7 +2,7 @@
namespace Ombi.Schedule.Jobs namespace Ombi.Schedule.Jobs
{ {
public interface IPlexContentSync public interface IPlexContentSync : IBaseJob
{ {
Task CacheContent(); Task CacheContent();
} }

@ -1,8 +1,9 @@
using System.Threading.Tasks; using System;
using System.Threading.Tasks;
namespace Ombi.Schedule.Jobs.Plex namespace Ombi.Schedule.Jobs.Plex.Interfaces
{ {
public interface IPlexEpisodeSync public interface IPlexEpisodeSync : IBaseJob
{ {
Task Start(); Task Start();
} }

@ -2,7 +2,7 @@
namespace Ombi.Schedule.Jobs.Plex namespace Ombi.Schedule.Jobs.Plex
{ {
public interface IPlexUserImporter public interface IPlexUserImporter : IBaseJob
{ {
Task Start(); Task Start();
} }

@ -143,5 +143,25 @@ namespace Ombi.Schedule.Jobs.Plex
await _movieRepo.Save(); await _movieRepo.Save();
} }
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_movieRepo?.Dispose();
_repo?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

@ -36,6 +36,7 @@ using Ombi.Api.Plex.Models;
using Ombi.Core.Settings; using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External; using Ombi.Core.Settings.Models.External;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Schedule.Jobs.Plex.Interfaces;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
@ -303,5 +304,26 @@ namespace Ombi.Schedule.Jobs.Plex
} }
return plex.Enable; return plex.Enable;
} }
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
Plex?.Dispose();
Repo?.Dispose();
EpisodeSync?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

@ -3,12 +3,14 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Hangfire; using Hangfire;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Ombi.Api.Plex; using Ombi.Api.Plex;
using Ombi.Api.Plex.Models; using Ombi.Api.Plex.Models;
using Ombi.Core.Settings; using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External; using Ombi.Core.Settings.Models.External;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Schedule.Jobs.Plex.Interfaces;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Store.Repository; using Ombi.Store.Repository;
@ -99,7 +101,7 @@ namespace Ombi.Schedule.Jobs.Plex
var currentPosition = 0; var currentPosition = 0;
var resultCount = settings.EpisodeBatchSize == 0 ? 50 : settings.EpisodeBatchSize; var resultCount = settings.EpisodeBatchSize == 0 ? 50 : settings.EpisodeBatchSize;
var episodes = await _api.GetAllEpisodes(settings.PlexAuthToken, settings.FullUri, section.key, currentPosition, resultCount); var episodes = await _api.GetAllEpisodes(settings.PlexAuthToken, settings.FullUri, section.key, currentPosition, resultCount);
var currentData = _repo.GetAllEpisodes(); var currentData = _repo.GetAllEpisodes().AsNoTracking();
_log.LogInformation(LoggingEvents.PlexEpisodeCacher, $"Total Epsiodes found for {episodes.MediaContainer.librarySectionTitle} = {episodes.MediaContainer.totalSize}"); _log.LogInformation(LoggingEvents.PlexEpisodeCacher, $"Total Epsiodes found for {episodes.MediaContainer.librarySectionTitle} = {episodes.MediaContainer.totalSize}");
await ProcessEpsiodes(episodes, currentData); await ProcessEpsiodes(episodes, currentData);
@ -159,5 +161,25 @@ namespace Ombi.Schedule.Jobs.Plex
return true; return true;
} }
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_repo?.Dispose();
_settings?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

@ -169,5 +169,26 @@ namespace Ombi.Schedule.Jobs.Plex
} }
return result.Succeeded; return result.Succeeded;
} }
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_userManager?.Dispose();
_plexSettings?.Dispose();
_userManagementSettings?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

@ -1,8 +1,9 @@
using System.Threading.Tasks; using System;
using System.Threading.Tasks;
namespace Ombi.Core.Settings namespace Ombi.Core.Settings
{ {
public interface ISettingsService<T> public interface ISettingsService<T> : IDisposable
{ {
T GetSettings(); T GetSettings();
Task<T> GetSettingsAsync(); Task<T> GetSettingsAsync();

@ -155,5 +155,24 @@ namespace Ombi.Settings.Settings
return settings.Content; return settings.Content;
//return _protector.Unprotect(settings.Content); //return _protector.Unprotect(settings.Content);
} }
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
Repo?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

@ -18,7 +18,6 @@ namespace Ombi.Store.Context
_created = true; _created = true;
Database.Migrate(); Database.Migrate();
} }
public DbSet<NotificationTemplates> NotificationTemplates { get; set; } public DbSet<NotificationTemplates> NotificationTemplates { get; set; }

@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -109,5 +110,24 @@ namespace Ombi.Store.Repository
Db.EmbyEpisode.AddRange(content); Db.EmbyEpisode.AddRange(content);
await Db.SaveChangesAsync(); await Db.SaveChangesAsync();
} }
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
Db?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

@ -1,11 +1,12 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Store.Entities; using Ombi.Store.Entities;
namespace Ombi.Store.Repository namespace Ombi.Store.Repository
{ {
public interface IEmbyContentRepository public interface IEmbyContentRepository : IDisposable
{ {
Task<EmbyContent> Add(EmbyContent content); Task<EmbyContent> Add(EmbyContent content);
Task AddRange(IEnumerable<EmbyContent> content); Task AddRange(IEnumerable<EmbyContent> content);

@ -8,7 +8,7 @@ using Ombi.Store.Entities;
namespace Ombi.Store.Repository namespace Ombi.Store.Repository
{ {
public interface IRepository<T> where T : Entity public interface IRepository<T> : IDisposable where T : Entity
{ {
Task<T> Find(object key); Task<T> Find(object key);
IQueryable<T> GetAll(); IQueryable<T> GetAll();

@ -1,10 +1,11 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Store.Entities; using Ombi.Store.Entities;
namespace Ombi.Store.Repository namespace Ombi.Store.Repository
{ {
public interface ISettingsRepository public interface ISettingsRepository : IDisposable
{ {
/// <summary> /// <summary>
/// Inserts the specified entity. /// Inserts the specified entity.

@ -71,5 +71,27 @@ namespace Ombi.Store.Repository
{ {
return source.Include(navigationPropertyPath); return source.Include(navigationPropertyPath);
} }
private bool _disposed;
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_ctx?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }

@ -89,5 +89,24 @@ namespace Ombi.Store.Repository
{ {
return $"{entity}Json"; return $"{entity}Json";
} }
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
Db?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
} }
Loading…
Cancel
Save