mirror of
https://github.com/sphildreth/roadie
synced 2024-11-10 06:44:12 +00:00
WIP Fix for EF with accessing disposed context
This commit is contained in:
parent
41c0cb9007
commit
97483dee11
15 changed files with 104 additions and 98 deletions
|
@ -1,18 +1,13 @@
|
|||
using Mapster;
|
||||
using Microsoft.AspNet.OData;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Api.Services;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using models = Roadie.Library.Models;
|
||||
|
||||
namespace Roadie.Api.Controllers
|
||||
{
|
||||
|
@ -22,19 +17,13 @@ namespace Roadie.Api.Controllers
|
|||
[Authorize]
|
||||
public class ArtistController : EntityControllerBase
|
||||
{
|
||||
private readonly IArtistService _artistService;
|
||||
private IArtistService ArtistService
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._artistService;
|
||||
}
|
||||
}
|
||||
private IArtistService ArtistService { get; }
|
||||
|
||||
public ArtistController(IArtistService artistService, ILoggerFactory logger, ICacheManager cacheManager, IConfiguration configuration)
|
||||
: base(cacheManager, configuration)
|
||||
{
|
||||
this._logger = logger.CreateLogger("RoadieApi.Controllers.ArtistController");
|
||||
this._artistService = artistService;
|
||||
this.ArtistService = artistService;
|
||||
}
|
||||
|
||||
//[EnableQuery]
|
||||
|
@ -46,18 +35,17 @@ namespace Roadie.Api.Controllers
|
|||
[HttpGet("{id}")]
|
||||
[ProducesResponseType(200)]
|
||||
[ProducesResponseType(404)]
|
||||
public async Task<IActionResult> Get(Guid id, string inc = null)
|
||||
public async Task<ActionResult<Artist>> Get(Guid id, string inc = null)
|
||||
{
|
||||
var key = id.ToString();
|
||||
var result = await this._cacheManager.GetAsync<Artist>(key, async () =>
|
||||
{
|
||||
var op = await this.ArtistService.ArtistById(null, id, (inc ?? "stats,imaes,associatedartists,collections,playlists,contributions,labels").ToLower().Split(","));
|
||||
return op.Data;
|
||||
}, key);
|
||||
var result = await this.ArtistService.ArtistById(null, id, (inc ?? Artist.DefaultIncludes).ToLower().Split(","));
|
||||
if (result == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ using Roadie.Library.Data;
|
|||
|
||||
namespace Roadie.Api.Controllers
|
||||
{
|
||||
public abstract class EntityControllerBase : ODataController
|
||||
public abstract class EntityControllerBase : ODataController
|
||||
{
|
||||
protected readonly ICacheManager _cacheManager;
|
||||
protected readonly IConfiguration _configuration;
|
||||
|
@ -16,13 +16,7 @@ namespace Roadie.Api.Controllers
|
|||
|
||||
protected ILogger _logger;
|
||||
|
||||
protected IRoadieSettings RoadieSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._roadieSettings;
|
||||
}
|
||||
}
|
||||
protected IRoadieSettings RoadieSettings => this._roadieSettings;
|
||||
|
||||
public EntityControllerBase(ICacheManager cacheManager, IConfiguration configuration)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Mapster;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
|
@ -47,21 +48,29 @@ namespace Roadie.Api.Services
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public async Task<OperationResult<Artist>> ArtistById(User roadieUser, Guid id, IEnumerable<string> includes)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
sw.Start();
|
||||
var cacheKey = string.Format("urn:artist_by_id_operation:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes));
|
||||
var result = await this.CacheManager.GetAsync<OperationResult<Artist>>(cacheKey, async () => {
|
||||
return await this.ArtistByIdAction(roadieUser, id, includes);
|
||||
}, data.Artist.CacheRegionKey(id));
|
||||
sw.Stop();
|
||||
return new OperationResult<Artist>(result.Messages)
|
||||
{
|
||||
Data = result.Data,
|
||||
Errors = result.Errors,
|
||||
IsSuccess = result != null,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<OperationResult<Artist>> ArtistByIdAction(User roadieUser, Guid id, IEnumerable<string> includes)
|
||||
{
|
||||
roadieUser = roadieUser ?? new User();
|
||||
var sw = Stopwatch.StartNew();
|
||||
sw.Start();
|
||||
var cacheRegion = (new data.Artist { RoadieId = id }).CacheRegion;
|
||||
var cacheKey = string.Format("urn:artist_result_model:{0}:{1}", id, includes == null ? "0" : string.Join("|", includes));
|
||||
var resultInCache = this._cacheManager.Get<OperationResult<Artist>>(cacheKey, cacheRegion);
|
||||
if (resultInCache != null)
|
||||
{
|
||||
sw.Stop();
|
||||
resultInCache.OperationTime = sw.ElapsedMilliseconds;
|
||||
return resultInCache;
|
||||
}
|
||||
|
||||
var artist = this.DbContext.Artists
|
||||
.Include(x => x.Genres)
|
||||
|
@ -284,14 +293,12 @@ namespace Roadie.Api.Services
|
|||
}
|
||||
}
|
||||
sw.Stop();
|
||||
resultInCache = new OperationResult<Artist>
|
||||
return new OperationResult<Artist>
|
||||
{
|
||||
Data = result,
|
||||
IsSuccess = result != null,
|
||||
OperationTime = sw.ElapsedMilliseconds
|
||||
};
|
||||
this._cacheManager.Add(cacheKey, resultInCache, cacheRegion);
|
||||
return resultInCache;
|
||||
}
|
||||
|
||||
public async Task<OperationResult<Artist>> ArtistByName(string name, IEnumerable<string> includes)
|
||||
|
|
|
@ -129,14 +129,16 @@ namespace Roadie.Api
|
|||
// return settings;
|
||||
//});
|
||||
|
||||
var cacheManager = new MemoryCacheManager(this._loggerFactory.CreateLogger<MemoryCacheManager>(), new CachePolicy(TimeSpan.FromHours(1)));
|
||||
var cacheManager = new MemoryCacheManager(this._loggerFactory.CreateLogger<MemoryCacheManager>(), new CachePolicy(TimeSpan.FromHours(4)));
|
||||
services.AddSingleton<ICacheManager>(cacheManager);
|
||||
|
||||
services.AddEntityFrameworkMySql().AddDbContext<ApplicationUserDbContext>(options =>
|
||||
options.UseMySql(this._configuration.GetConnectionString("RoadieDatabaseConnection")));
|
||||
services.AddDbContextPool<ApplicationUserDbContext>(
|
||||
options => options.UseMySql(this._configuration.GetConnectionString("RoadieDatabaseConnection")
|
||||
));
|
||||
|
||||
services.AddEntityFrameworkMySql().AddDbContext<IRoadieDbContext, RoadieDbContext>(options =>
|
||||
options.UseMySql(this._configuration.GetConnectionString("RoadieDatabaseConnection")));
|
||||
services.AddDbContextPool<IRoadieDbContext, RoadieDbContext>(
|
||||
options => options.UseMySql(this._configuration.GetConnectionString("RoadieDatabaseConnection")
|
||||
));
|
||||
|
||||
services.AddIdentity<ApplicationUser, ApplicationRole>()
|
||||
.AddEntityFrameworkStores<ApplicationUserDbContext>()
|
||||
|
@ -164,7 +166,7 @@ namespace Roadie.Api
|
|||
services.AddScoped<IPlaylistService, PlaylistService>();
|
||||
services.AddScoped<IArtistService, ArtistService>();
|
||||
|
||||
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(this._configuration["Tokens:PrivateKey"]));
|
||||
var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(this._configuration["Tokens:PrivateKey"]));
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
|
@ -204,7 +206,7 @@ namespace Roadie.Api
|
|||
})
|
||||
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
||||
|
||||
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddScoped<IHttpContext>(factory =>
|
||||
{
|
||||
var actionContext = factory.GetService<IActionContextAccessor>()
|
||||
|
|
|
@ -34,8 +34,6 @@ namespace Roadie.Library.Caching
|
|||
|
||||
public abstract void ClearRegion(string region);
|
||||
|
||||
public abstract void Dispose();
|
||||
|
||||
public abstract bool Exists<TOut>(string key);
|
||||
|
||||
public abstract bool Exists<TOut>(string key, string region);
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Roadie.Library.Caching
|
||||
{
|
||||
public interface ICacheManager : IDisposable
|
||||
public interface ICacheManager
|
||||
{
|
||||
bool Add<TCacheValue>(string key, TCacheValue value);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Library.Caching
|
||||
|
@ -17,32 +18,23 @@ namespace Roadie.Library.Caching
|
|||
|
||||
public override bool Add<TCacheValue>(string key, TCacheValue value)
|
||||
{
|
||||
using (var entry = _cache.CreateEntry(key))
|
||||
{
|
||||
_cache.Set(key, value);
|
||||
return true;
|
||||
}
|
||||
_cache.Set(key, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Add<TCacheValue>(string key, TCacheValue value, string region)
|
||||
{
|
||||
using (var entry = _cache.CreateEntry(key))
|
||||
{
|
||||
_cache.Set(key, value, DateTimeOffset.MaxValue);
|
||||
return true;
|
||||
}
|
||||
_cache.Set(key, value, this._defaultPolicy.ExpiresAfter);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Add<TCacheValue>(string key, TCacheValue value, CachePolicy policy)
|
||||
{
|
||||
using (var entry = _cache.CreateEntry(key))
|
||||
_cache.Set(key, value, new MemoryCacheEntryOptions
|
||||
{
|
||||
_cache.Set(key, value, new MemoryCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpiration = DateTimeOffset.UtcNow.Add(policy.ExpiresAfter) // new DateTimeOffset(DateTime.UtcNow, policy.ExpiresAfter)
|
||||
});
|
||||
return true;
|
||||
}
|
||||
AbsoluteExpiration = DateTimeOffset.UtcNow.Add(policy.ExpiresAfter) // new DateTimeOffset(DateTime.UtcNow, policy.ExpiresAfter)
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Add<TCacheValue>(string key, TCacheValue value, string region, CachePolicy policy)
|
||||
|
@ -60,11 +52,6 @@ namespace Roadie.Library.Caching
|
|||
this.Clear();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
// throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool Exists<TOut>(string key)
|
||||
{
|
||||
return this.Get<TOut>(key) != null;
|
||||
|
@ -119,6 +106,11 @@ namespace Roadie.Library.Caching
|
|||
{
|
||||
r = await getItem();
|
||||
this.Add(key, r, region);
|
||||
Trace.WriteLine($"-+> Cache Miss for Key [{ key }], Region [{ region }]");
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.WriteLine($"-!> Cache Hit for Key [{ key }], Region [{ region }]");
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -83,13 +83,6 @@ namespace Roadie.Library.Caching
|
|||
this.Clear();
|
||||
}
|
||||
|
||||
// Dispose() calls Dispose(true)
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public override bool Exists<TOut>(string key)
|
||||
{
|
||||
return this.Exists<TOut>(key, null);
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace Roadie.Library.Data
|
|||
[Column("artistType", TypeName = "enum")]
|
||||
public string ArtistType { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public virtual ICollection<ArtistAssociation> AssociatedArtists { get; set; }
|
||||
|
||||
[Column("bandStatus", TypeName = "enum")]
|
||||
|
|
|
@ -7,11 +7,16 @@ namespace Roadie.Library.Data
|
|||
{
|
||||
public partial class Artist
|
||||
{
|
||||
public static string CacheRegionKey(Guid Id)
|
||||
{
|
||||
return string.Format("urn:artist:{0}", Id);
|
||||
}
|
||||
|
||||
public string CacheRegion
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format("urn:artist:{0}", this.RoadieId);
|
||||
return Artist.CacheRegionKey(this.RoadieId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,16 @@ namespace Roadie.Library.Data
|
|||
{
|
||||
public partial class Label
|
||||
{
|
||||
public static string CacheRegionKey(Guid Id)
|
||||
{
|
||||
return string.Format("urn:label:{0}", Id);
|
||||
}
|
||||
|
||||
public string CacheRegion
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format("urn:label:{0}", this.RoadieId);
|
||||
return Label.CacheRegionKey(this.RoadieId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,11 +8,16 @@ namespace Roadie.Library.Data
|
|||
{
|
||||
public partial class Release
|
||||
{
|
||||
public static string CacheRegionKey(Guid Id)
|
||||
{
|
||||
return string.Format("urn:release:{0}", Id);
|
||||
}
|
||||
|
||||
public string CacheRegion
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format("urn:release:{0}", this.RoadieId);
|
||||
return Release.CacheRegionKey(this.RoadieId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,11 +10,16 @@ namespace Roadie.Library.Data
|
|||
{
|
||||
public partial class Track
|
||||
{
|
||||
public static string CacheRegionKey(Guid Id)
|
||||
{
|
||||
return string.Format("urn:track:{0}", Id);
|
||||
}
|
||||
|
||||
public string CacheRegion
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format("urn:track:{0}", this.RoadieId);
|
||||
return Track.CacheRegionKey(this.RoadieId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ namespace Roadie.Library.Models
|
|||
[Serializable]
|
||||
public class Artist : EntityModelBase
|
||||
{
|
||||
public const string DefaultIncludes = "stats,imaes,associatedartists,collections,playlists,contributions,labels";
|
||||
|
||||
public IEnumerable<ReleaseList> ArtistContributionReleases;
|
||||
public IEnumerable<LabelList> ArtistLabels;
|
||||
|
||||
|
|
|
@ -12,13 +12,13 @@ namespace Roadie.Library
|
|||
public const string NotModified = "NotModified";
|
||||
public const string OkMessage = "OK";
|
||||
|
||||
private List<string> _messages = new List<string>();
|
||||
private List<Exception> _errors = new List<Exception>();
|
||||
|
||||
private List<string> _messages = new List<string>();
|
||||
public Dictionary<string, object> AdditionalData { get; set; }
|
||||
public T Data { get; set; }
|
||||
public IEnumerable<Exception> Errors { get; set; }
|
||||
public bool IsSuccess { get; set; }
|
||||
|
||||
public IEnumerable<string> Messages
|
||||
{
|
||||
get
|
||||
|
@ -26,12 +26,22 @@ namespace Roadie.Library
|
|||
return this._messages;
|
||||
}
|
||||
}
|
||||
|
||||
public long OperationTime { get; set; }
|
||||
|
||||
public OperationResult()
|
||||
{
|
||||
}
|
||||
|
||||
public OperationResult(IEnumerable<string> messages = null)
|
||||
{
|
||||
if (messages != null && messages.Any())
|
||||
{
|
||||
this.AdditionalData = new Dictionary<string, object>();
|
||||
messages.ToList().ForEach(x => this.AddMessage(x));
|
||||
}
|
||||
}
|
||||
|
||||
public OperationResult(string message = null)
|
||||
{
|
||||
this.AdditionalData = new Dictionary<string, object>();
|
||||
|
@ -49,21 +59,20 @@ namespace Roadie.Library
|
|||
this.AddError(error);
|
||||
}
|
||||
|
||||
public void AddMessage(string message)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(message))
|
||||
{
|
||||
this._messages.Add(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddError(Exception exception)
|
||||
{
|
||||
if(exception != null)
|
||||
if (exception != null)
|
||||
{
|
||||
this._errors.Add(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddMessage(string message)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
this._messages.Add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue