Use EF Core for Activity database
This commit is contained in:
parent
bd550ef996
commit
f6f0a8a481
|
@ -1,9 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Model.Activity;
|
using MediaBrowser.Model.Activity;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
using MediaBrowser.Model.Querying;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Activity
|
namespace Emby.Server.Implementations.Activity
|
||||||
|
@ -26,20 +27,38 @@ namespace Emby.Server.Implementations.Activity
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Create(ActivityLogEntry entry)
|
public async Task Create(ActivityLogEntry entry)
|
||||||
{
|
{
|
||||||
entry.Date = DateTime.UtcNow;
|
entry.Date = DateTime.UtcNow;
|
||||||
|
|
||||||
_repo.Create(entry);
|
await _repo.CreateAsync(entry);
|
||||||
|
|
||||||
EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(entry));
|
EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
|
public IEnumerable<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
|
||||||
{
|
{
|
||||||
var result = _repo.GetActivityLogEntries(minDate, hasUserId, startIndex, limit);
|
var result = _repo.GetActivityLogEntries();
|
||||||
|
|
||||||
foreach (var item in result.Items.Where(i => !i.UserId.Equals(Guid.Empty)))
|
if (minDate.HasValue)
|
||||||
|
{
|
||||||
|
result = result.Where(x => x.Date >= minDate.Value);
|
||||||
|
}
|
||||||
|
if (hasUserId.HasValue)
|
||||||
|
{
|
||||||
|
result = result.Where(x => x.UserId != null && x.UserId != Guid.Empty);
|
||||||
|
}
|
||||||
|
if (startIndex.HasValue)
|
||||||
|
{
|
||||||
|
result = result.Where(x => x.Id >= startIndex.Value);
|
||||||
|
}
|
||||||
|
if (limit.HasValue)
|
||||||
|
{
|
||||||
|
result = result.Take(limit.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add images for each user
|
||||||
|
foreach (var item in result)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(item.UserId);
|
var user = _userManager.GetUserById(item.UserId);
|
||||||
|
|
||||||
|
@ -52,10 +71,5 @@ namespace Emby.Server.Implementations.Activity
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
|
|
||||||
{
|
|
||||||
return GetActivityLogEntries(minDate, null, startIndex, limit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,310 +1,37 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Emby.Server.Implementations.Data;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller;
|
|
||||||
using MediaBrowser.Model.Activity;
|
using MediaBrowser.Model.Activity;
|
||||||
using MediaBrowser.Model.IO;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MediaBrowser.Model.Querying;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using SQLitePCL.pretty;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Activity
|
namespace Emby.Server.Implementations.Activity
|
||||||
{
|
{
|
||||||
public class ActivityRepository : BaseSqliteRepository, IActivityRepository
|
public class ActivityRepository : DbContext, IActivityRepository
|
||||||
{
|
{
|
||||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
protected string _dataDirPath;
|
||||||
protected IFileSystem FileSystem { get; private set; }
|
|
||||||
|
|
||||||
public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem)
|
public DbSet<ActivityLogEntry> ActivityLogs { get; set; }
|
||||||
: base(loggerFactory.CreateLogger(nameof(ActivityRepository)))
|
|
||||||
|
public ActivityRepository(string dataDirPath)
|
||||||
{
|
{
|
||||||
DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
|
_dataDirPath = dataDirPath;
|
||||||
FileSystem = fileSystem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize()
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
try
|
// Ensure the dir exists
|
||||||
{
|
if (!Directory.Exists(_dataDirPath)) Directory.CreateDirectory(_dataDirPath);
|
||||||
InitializeInternal();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError(ex, "Error loading database file. Will reset and retry.");
|
|
||||||
|
|
||||||
FileSystem.DeleteFile(DbFilePath);
|
optionsBuilder.UseSqlite($"Filename={Path.Combine(_dataDirPath, "activitylog.sqlite.db")}");
|
||||||
|
|
||||||
InitializeInternal();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeInternal()
|
public async Task CreateAsync(ActivityLogEntry entry)
|
||||||
{
|
{
|
||||||
using (var connection = CreateConnection())
|
await ActivityLogs.AddAsync(entry);
|
||||||
{
|
await SaveChangesAsync();
|
||||||
RunDefaultInitialization(connection);
|
|
||||||
|
|
||||||
connection.RunQueries(new[]
|
|
||||||
{
|
|
||||||
"create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)",
|
|
||||||
"drop index if exists idx_ActivityLogEntries"
|
|
||||||
});
|
|
||||||
|
|
||||||
TryMigrate(connection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryMigrate(ManagedConnection connection)
|
public IQueryable<ActivityLogEntry> GetActivityLogEntries()
|
||||||
{
|
=> ActivityLogs;
|
||||||
try
|
|
||||||
{
|
|
||||||
if (TableExists(connection, "ActivityLogEntries"))
|
|
||||||
{
|
|
||||||
connection.RunQueries(new[]
|
|
||||||
{
|
|
||||||
"INSERT INTO ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) SELECT Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity FROM ActivityLogEntries",
|
|
||||||
"drop table if exists ActivityLogEntries"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError(ex, "Error migrating activity log database");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog";
|
|
||||||
|
|
||||||
public void Create(ActivityLogEntry entry)
|
|
||||||
{
|
|
||||||
if (entry == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
using (WriteLock.Write())
|
|
||||||
using (var connection = CreateConnection())
|
|
||||||
{
|
|
||||||
connection.RunInTransaction(db =>
|
|
||||||
{
|
|
||||||
using (var statement = db.PrepareStatement("insert into ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"))
|
|
||||||
{
|
|
||||||
statement.TryBind("@Name", entry.Name);
|
|
||||||
|
|
||||||
statement.TryBind("@Overview", entry.Overview);
|
|
||||||
statement.TryBind("@ShortOverview", entry.ShortOverview);
|
|
||||||
statement.TryBind("@Type", entry.Type);
|
|
||||||
statement.TryBind("@ItemId", entry.ItemId);
|
|
||||||
|
|
||||||
if (entry.UserId.Equals(Guid.Empty))
|
|
||||||
{
|
|
||||||
statement.TryBindNull("@UserId");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
statement.TryBind("@UserId", entry.UserId.ToString("N"));
|
|
||||||
}
|
|
||||||
|
|
||||||
statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
|
|
||||||
statement.TryBind("@LogSeverity", entry.Severity.ToString());
|
|
||||||
|
|
||||||
statement.MoveNext();
|
|
||||||
}
|
|
||||||
}, TransactionMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(ActivityLogEntry entry)
|
|
||||||
{
|
|
||||||
if (entry == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
using (WriteLock.Write())
|
|
||||||
using (var connection = CreateConnection())
|
|
||||||
{
|
|
||||||
connection.RunInTransaction(db =>
|
|
||||||
{
|
|
||||||
using (var statement = db.PrepareStatement("Update ActivityLog set Name=@Name,Overview=@Overview,ShortOverview=@ShortOverview,Type=@Type,ItemId=@ItemId,UserId=@UserId,DateCreated=@DateCreated,LogSeverity=@LogSeverity where Id=@Id"))
|
|
||||||
{
|
|
||||||
statement.TryBind("@Id", entry.Id);
|
|
||||||
|
|
||||||
statement.TryBind("@Name", entry.Name);
|
|
||||||
statement.TryBind("@Overview", entry.Overview);
|
|
||||||
statement.TryBind("@ShortOverview", entry.ShortOverview);
|
|
||||||
statement.TryBind("@Type", entry.Type);
|
|
||||||
statement.TryBind("@ItemId", entry.ItemId);
|
|
||||||
|
|
||||||
if (entry.UserId.Equals(Guid.Empty))
|
|
||||||
{
|
|
||||||
statement.TryBindNull("@UserId");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
statement.TryBind("@UserId", entry.UserId.ToString("N"));
|
|
||||||
}
|
|
||||||
|
|
||||||
statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
|
|
||||||
statement.TryBind("@LogSeverity", entry.Severity.ToString());
|
|
||||||
|
|
||||||
statement.MoveNext();
|
|
||||||
}
|
|
||||||
}, TransactionMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
|
|
||||||
{
|
|
||||||
using (WriteLock.Read())
|
|
||||||
using (var connection = CreateConnection(true))
|
|
||||||
{
|
|
||||||
var commandText = BaseActivitySelectText;
|
|
||||||
var whereClauses = new List<string>();
|
|
||||||
|
|
||||||
if (minDate.HasValue)
|
|
||||||
{
|
|
||||||
whereClauses.Add("DateCreated>=@DateCreated");
|
|
||||||
}
|
|
||||||
if (hasUserId.HasValue)
|
|
||||||
{
|
|
||||||
if (hasUserId.Value)
|
|
||||||
{
|
|
||||||
whereClauses.Add("UserId not null");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
whereClauses.Add("UserId is null");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var whereTextWithoutPaging = whereClauses.Count == 0 ?
|
|
||||||
string.Empty :
|
|
||||||
" where " + string.Join(" AND ", whereClauses.ToArray());
|
|
||||||
|
|
||||||
if (startIndex.HasValue && startIndex.Value > 0)
|
|
||||||
{
|
|
||||||
var pagingWhereText = whereClauses.Count == 0 ?
|
|
||||||
string.Empty :
|
|
||||||
" where " + string.Join(" AND ", whereClauses.ToArray());
|
|
||||||
|
|
||||||
whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})",
|
|
||||||
pagingWhereText,
|
|
||||||
startIndex.Value.ToString(_usCulture)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var whereText = whereClauses.Count == 0 ?
|
|
||||||
string.Empty :
|
|
||||||
" where " + string.Join(" AND ", whereClauses.ToArray());
|
|
||||||
|
|
||||||
commandText += whereText;
|
|
||||||
|
|
||||||
commandText += " ORDER BY DateCreated DESC";
|
|
||||||
|
|
||||||
if (limit.HasValue)
|
|
||||||
{
|
|
||||||
commandText += " LIMIT " + limit.Value.ToString(_usCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
var statementTexts = new List<string>();
|
|
||||||
statementTexts.Add(commandText);
|
|
||||||
statementTexts.Add("select count (Id) from ActivityLog" + whereTextWithoutPaging);
|
|
||||||
|
|
||||||
return connection.RunInTransaction(db =>
|
|
||||||
{
|
|
||||||
var list = new List<ActivityLogEntry>();
|
|
||||||
var result = new QueryResult<ActivityLogEntry>();
|
|
||||||
|
|
||||||
var statements = PrepareAllSafe(db, statementTexts).ToList();
|
|
||||||
|
|
||||||
using (var statement = statements[0])
|
|
||||||
{
|
|
||||||
if (minDate.HasValue)
|
|
||||||
{
|
|
||||||
statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var row in statement.ExecuteQuery())
|
|
||||||
{
|
|
||||||
list.Add(GetEntry(row));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var statement = statements[1])
|
|
||||||
{
|
|
||||||
if (minDate.HasValue)
|
|
||||||
{
|
|
||||||
statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Items = list.ToArray();
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}, ReadTransactionMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ActivityLogEntry GetEntry(IReadOnlyList<IResultSetValue> reader)
|
|
||||||
{
|
|
||||||
var index = 0;
|
|
||||||
|
|
||||||
var info = new ActivityLogEntry
|
|
||||||
{
|
|
||||||
Id = reader[index].ToInt64()
|
|
||||||
};
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.Name = reader[index].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.Overview = reader[index].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.ShortOverview = reader[index].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.Type = reader[index].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.ItemId = reader[index].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.UserId = new Guid(reader[index].ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
info.Date = reader[index].ReadDateTime();
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.Severity = (LogLevel)Enum.Parse(typeof(LogLevel), reader[index].ToString(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1148,11 +1148,7 @@ namespace Emby.Server.Implementations
|
||||||
|
|
||||||
private IActivityRepository GetActivityLogRepository()
|
private IActivityRepository GetActivityLogRepository()
|
||||||
{
|
{
|
||||||
var repo = new ActivityRepository(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager);
|
return new ActivityRepository(ServerConfigurationManager.ApplicationPaths.DataPath);
|
||||||
|
|
||||||
repo.Initialize();
|
|
||||||
|
|
||||||
return repo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.0" />
|
||||||
<PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
|
<PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
|
||||||
<PackageReference Include="sharpcompress" Version="0.22.0" />
|
<PackageReference Include="sharpcompress" Version="0.22.0" />
|
||||||
<PackageReference Include="SimpleInjector" Version="4.4.2" />
|
<PackageReference Include="SimpleInjector" Version="4.4.2" />
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
using MediaBrowser.Model.Querying;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Activity
|
namespace MediaBrowser.Model.Activity
|
||||||
{
|
{
|
||||||
|
@ -8,10 +9,8 @@ namespace MediaBrowser.Model.Activity
|
||||||
{
|
{
|
||||||
event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
|
event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
|
||||||
|
|
||||||
void Create(ActivityLogEntry entry);
|
Task Create(ActivityLogEntry entry);
|
||||||
|
|
||||||
QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit);
|
IEnumerable<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? x, int? y);
|
||||||
|
|
||||||
QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? x, int? y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
using System;
|
using System.Linq;
|
||||||
using MediaBrowser.Model.Querying;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Activity
|
namespace MediaBrowser.Model.Activity
|
||||||
{
|
{
|
||||||
public interface IActivityRepository
|
public interface IActivityRepository
|
||||||
{
|
{
|
||||||
void Create(ActivityLogEntry entry);
|
Task CreateAsync(ActivityLogEntry entry);
|
||||||
|
|
||||||
QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? z, int? startIndex, int? limit);
|
IQueryable<ActivityLogEntry> GetActivityLogEntries();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user