2018-11-05 22:01:29 -05:00
|
|
|
|
using Microsoft.Data.Sqlite;
|
2018-04-08 02:44:42 -04:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
2019-07-24 20:15:07 -04:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using Microsoft.Extensions.Logging.Console;
|
2018-04-08 02:44:42 -04:00
|
|
|
|
using SharedLibraryCore.Database.Models;
|
2018-11-05 22:01:29 -05:00
|
|
|
|
using SharedLibraryCore.Interfaces;
|
|
|
|
|
using System;
|
2018-04-08 02:44:42 -04:00
|
|
|
|
using System.Collections.Generic;
|
2018-11-05 22:01:29 -05:00
|
|
|
|
using System.IO;
|
2018-04-08 02:44:42 -04:00
|
|
|
|
using System.Linq;
|
2018-11-05 22:01:29 -05:00
|
|
|
|
using System.Reflection;
|
2018-08-01 22:09:22 -04:00
|
|
|
|
using System.Runtime.InteropServices;
|
2019-11-25 13:05:12 -05:00
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
2017-11-25 20:29:58 -05:00
|
|
|
|
|
2018-04-08 02:44:42 -04:00
|
|
|
|
namespace SharedLibraryCore.Database
|
2017-11-25 20:29:58 -05:00
|
|
|
|
{
|
2017-11-29 19:35:50 -05:00
|
|
|
|
public class DatabaseContext : DbContext
|
2017-11-25 20:29:58 -05:00
|
|
|
|
{
|
|
|
|
|
public DbSet<EFClient> Clients { get; set; }
|
|
|
|
|
public DbSet<EFAlias> Aliases { get; set; }
|
|
|
|
|
public DbSet<EFAliasLink> AliasLinks { get; set; }
|
|
|
|
|
public DbSet<EFPenalty> Penalties { get; set; }
|
2018-06-02 00:48:10 -04:00
|
|
|
|
public DbSet<EFMeta> EFMeta { get; set; }
|
2018-06-16 22:11:25 -04:00
|
|
|
|
public DbSet<EFChangeHistory> EFChangeHistory { get; set; }
|
2017-11-25 20:29:58 -05:00
|
|
|
|
|
2018-10-02 13:39:08 -04:00
|
|
|
|
static string _ConnectionString;
|
|
|
|
|
static string _provider;
|
2019-11-15 15:50:20 -05:00
|
|
|
|
private static readonly ILoggerFactory _loggerFactory = LoggerFactory.Create(builder =>
|
|
|
|
|
{
|
|
|
|
|
builder.AddConsole()
|
|
|
|
|
.AddDebug()
|
|
|
|
|
.AddFilter((category, level) => true);
|
|
|
|
|
});
|
2018-04-25 02:38:59 -04:00
|
|
|
|
|
2019-04-02 21:20:37 -04:00
|
|
|
|
public DatabaseContext(DbContextOptions<DatabaseContext> opt) : base(opt)
|
|
|
|
|
{
|
|
|
|
|
}
|
2018-04-06 20:15:17 -04:00
|
|
|
|
|
2019-04-02 21:20:37 -04:00
|
|
|
|
public DatabaseContext()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Dispose()
|
|
|
|
|
{
|
|
|
|
|
}
|
2018-09-07 23:29:42 -04:00
|
|
|
|
|
2019-04-02 21:20:37 -04:00
|
|
|
|
public DatabaseContext(bool disableTracking) : this()
|
2018-09-04 22:07:34 -04:00
|
|
|
|
{
|
|
|
|
|
if (disableTracking)
|
|
|
|
|
{
|
|
|
|
|
this.ChangeTracker.AutoDetectChangesEnabled = false;
|
|
|
|
|
this.ChangeTracker.LazyLoadingEnabled = false;
|
|
|
|
|
this.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
|
|
|
|
}
|
2019-01-02 19:32:39 -05:00
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this.ChangeTracker.AutoDetectChangesEnabled = true;
|
|
|
|
|
this.ChangeTracker.LazyLoadingEnabled = true;
|
|
|
|
|
this.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll;
|
|
|
|
|
}
|
2018-09-04 22:07:34 -04:00
|
|
|
|
}
|
2018-08-03 18:10:20 -04:00
|
|
|
|
|
2019-04-02 21:20:37 -04:00
|
|
|
|
public DatabaseContext(string connStr, string provider) : this()
|
2018-04-25 02:38:59 -04:00
|
|
|
|
{
|
|
|
|
|
_ConnectionString = connStr;
|
2018-09-23 20:45:54 -04:00
|
|
|
|
_provider = provider;
|
2018-04-25 02:38:59 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-08 02:44:42 -04:00
|
|
|
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
2017-11-25 20:29:58 -05:00
|
|
|
|
{
|
2020-04-28 18:54:06 -04:00
|
|
|
|
//optionsBuilder.UseLoggerFactory(_loggerFactory)
|
2020-08-26 10:54:56 -04:00
|
|
|
|
// .EnableSensitiveDataLogging();
|
2019-11-15 15:50:20 -05:00
|
|
|
|
|
2018-04-25 02:38:59 -04:00
|
|
|
|
if (string.IsNullOrEmpty(_ConnectionString))
|
|
|
|
|
{
|
2018-10-05 23:10:39 -04:00
|
|
|
|
string currentPath = Utilities.OperatingDirectory;
|
2018-08-01 22:09:22 -04:00
|
|
|
|
// allows the application to find the database file
|
|
|
|
|
currentPath = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
|
|
|
|
$"{Path.DirectorySeparatorChar}{currentPath}" :
|
|
|
|
|
currentPath;
|
2018-08-28 17:32:59 -04:00
|
|
|
|
|
2018-11-05 22:01:29 -05:00
|
|
|
|
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = Path.Join(currentPath, "Database", "Database.db") };
|
2018-04-25 02:38:59 -04:00
|
|
|
|
var connectionString = connectionStringBuilder.ToString();
|
|
|
|
|
var connection = new SqliteConnection(connectionString);
|
2018-10-05 23:10:39 -04:00
|
|
|
|
|
2020-04-25 20:01:26 -04:00
|
|
|
|
if (!optionsBuilder.IsConfigured)
|
|
|
|
|
{
|
|
|
|
|
optionsBuilder.UseSqlite(connection);
|
|
|
|
|
}
|
2018-04-25 02:38:59 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
2018-09-23 20:45:54 -04:00
|
|
|
|
switch (_provider)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
case "mysql":
|
2020-04-20 11:45:58 -04:00
|
|
|
|
optionsBuilder.UseMySql(_ConnectionString, _options => _options.EnableRetryOnFailure());
|
2018-09-23 20:45:54 -04:00
|
|
|
|
break;
|
|
|
|
|
case "postgresql":
|
2020-04-20 11:45:58 -04:00
|
|
|
|
optionsBuilder.UseNpgsql(_ConnectionString, _options => _options.EnableRetryOnFailure());
|
2018-09-23 20:45:54 -04:00
|
|
|
|
break;
|
|
|
|
|
}
|
2018-04-25 02:38:59 -04:00
|
|
|
|
}
|
2018-04-08 02:44:42 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-25 13:05:12 -05:00
|
|
|
|
private void SetAuditColumns()
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
var entries = ChangeTracker
|
|
|
|
|
.Entries()
|
|
|
|
|
.Where(e => e.Entity is SharedEntity && (
|
|
|
|
|
e.State == EntityState.Added
|
|
|
|
|
|| e.State == EntityState.Modified)).ToList();
|
|
|
|
|
|
|
|
|
|
foreach (var entityEntry in entries)
|
|
|
|
|
{
|
|
|
|
|
if (entityEntry.State == EntityState.Added)
|
|
|
|
|
{
|
|
|
|
|
//((SharedEntity)entityEntry.Entity).CreatedDateTime = DateTime.UtcNow;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//((SharedEntity)entityEntry.Entity).UpdatedDateTime = DateTime.UtcNow;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
|
|
|
|
|
{
|
|
|
|
|
SetAuditColumns();
|
|
|
|
|
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override int SaveChanges()
|
|
|
|
|
{
|
|
|
|
|
SetAuditColumns();
|
|
|
|
|
return base.SaveChanges();
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-08 02:44:42 -04:00
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
|
|
|
{
|
|
|
|
|
// make network id unique
|
|
|
|
|
modelBuilder.Entity<EFClient>(entity =>
|
|
|
|
|
{
|
|
|
|
|
entity.HasIndex(e => e.NetworkId).IsUnique();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity<EFPenalty>(entity =>
|
|
|
|
|
{
|
|
|
|
|
entity.HasOne(p => p.Offender)
|
2017-11-29 19:35:50 -05:00
|
|
|
|
.WithMany(c => c.ReceivedPenalties)
|
|
|
|
|
.HasForeignKey(c => c.OffenderId)
|
2018-04-08 02:44:42 -04:00
|
|
|
|
.OnDelete(DeleteBehavior.Restrict);
|
2017-11-29 19:35:50 -05:00
|
|
|
|
|
2018-04-08 02:44:42 -04:00
|
|
|
|
entity.HasOne(p => p.Punisher)
|
|
|
|
|
.WithMany(p => p.AdministeredPenalties)
|
2017-11-29 19:35:50 -05:00
|
|
|
|
.HasForeignKey(c => c.PunisherId)
|
2018-04-08 02:44:42 -04:00
|
|
|
|
.OnDelete(DeleteBehavior.Restrict);
|
2018-10-15 20:51:04 -04:00
|
|
|
|
|
|
|
|
|
entity.Property(p => p.Expires)
|
|
|
|
|
.IsRequired(false);
|
2018-04-08 02:44:42 -04:00
|
|
|
|
});
|
2017-11-25 20:29:58 -05:00
|
|
|
|
|
2018-04-08 02:44:42 -04:00
|
|
|
|
modelBuilder.Entity<EFAliasLink>(entity =>
|
|
|
|
|
{
|
|
|
|
|
entity.HasMany(e => e.Children)
|
|
|
|
|
.WithOne(a => a.Link)
|
|
|
|
|
.HasForeignKey(k => k.LinkId)
|
|
|
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
|
});
|
2017-11-29 19:35:50 -05:00
|
|
|
|
|
2018-06-16 22:11:25 -04:00
|
|
|
|
modelBuilder.Entity<EFAlias>(ent =>
|
|
|
|
|
{
|
2018-11-25 21:00:36 -05:00
|
|
|
|
ent.Property(a => a.IPAddress).IsRequired(false);
|
2018-06-16 22:11:25 -04:00
|
|
|
|
ent.HasIndex(a => a.IPAddress);
|
2018-09-11 15:28:37 -04:00
|
|
|
|
ent.Property(a => a.Name).HasMaxLength(24);
|
|
|
|
|
ent.HasIndex(a => a.Name);
|
2019-08-02 19:04:34 -04:00
|
|
|
|
ent.Property(_alias => _alias.SearchableName).HasMaxLength(24);
|
|
|
|
|
ent.HasIndex(_alias => _alias.SearchableName);
|
2019-11-15 15:50:20 -05:00
|
|
|
|
ent.HasIndex(_alias => new { _alias.Name, _alias.IPAddress }).IsUnique();
|
2018-06-16 22:11:25 -04:00
|
|
|
|
});
|
|
|
|
|
|
2019-02-22 20:06:51 -05:00
|
|
|
|
modelBuilder.Entity<EFMeta>(ent =>
|
|
|
|
|
{
|
|
|
|
|
ent.HasIndex(_meta => _meta.Key);
|
|
|
|
|
});
|
|
|
|
|
|
2018-04-09 15:17:10 -04:00
|
|
|
|
// force full name for database conversion
|
|
|
|
|
modelBuilder.Entity<EFClient>().ToTable("EFClients");
|
|
|
|
|
modelBuilder.Entity<EFAlias>().ToTable("EFAlias");
|
|
|
|
|
modelBuilder.Entity<EFAliasLink>().ToTable("EFAliasLinks");
|
|
|
|
|
modelBuilder.Entity<EFPenalty>().ToTable("EFPenalties");
|
|
|
|
|
|
2018-08-02 21:52:35 -04:00
|
|
|
|
// adapted from
|
2017-11-25 20:29:58 -05:00
|
|
|
|
// https://aleemkhan.wordpress.com/2013/02/28/dynamically-adding-dbset-properties-in-dbcontext-for-entity-framework-code-first/
|
2020-08-26 10:54:56 -04:00
|
|
|
|
|
2018-11-05 22:01:29 -05:00
|
|
|
|
string pluginDir = Path.Join(Utilities.OperatingDirectory, "Plugins");
|
2020-08-26 10:54:56 -04:00
|
|
|
|
|
|
|
|
|
if (Utilities.IsDevelopment)
|
|
|
|
|
{
|
|
|
|
|
pluginDir = Path.Join(Utilities.OperatingDirectory, "..", "..", "..", "..", "BUILD", "Plugins");
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-17 11:47:56 -04:00
|
|
|
|
IEnumerable<string> directoryFiles = Enumerable.Empty<string>();
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
directoryFiles = Directory.GetFiles(pluginDir).Where(f => f.EndsWith(".dll"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
catch (DirectoryNotFoundException)
|
|
|
|
|
{
|
|
|
|
|
// this is just an ugly thing for unit testing
|
|
|
|
|
directoryFiles = Directory.GetFiles(@"X:\IW4MAdmin\Tests\ApplicationTests\bin\Debug\netcoreapp3.1").Where(f => f.EndsWith("dll"));
|
|
|
|
|
}
|
2018-04-05 00:38:45 -04:00
|
|
|
|
|
2018-09-23 20:45:54 -04:00
|
|
|
|
foreach (string dllPath in directoryFiles)
|
2018-02-07 00:19:06 -05:00
|
|
|
|
{
|
|
|
|
|
Assembly library;
|
|
|
|
|
try
|
|
|
|
|
{
|
2018-04-08 17:50:58 -04:00
|
|
|
|
library = Assembly.LoadFrom(dllPath);
|
2018-02-07 00:19:06 -05:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-19 01:48:14 -04:00
|
|
|
|
// not a valid assembly, ie plugin support files
|
2018-02-07 00:19:06 -05:00
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-04-25 02:38:59 -04:00
|
|
|
|
|
2018-04-08 17:50:58 -04:00
|
|
|
|
var configurations = library.ExportedTypes.Where(c => c.GetInterfaces().FirstOrDefault(i => typeof(IModelConfiguration).IsAssignableFrom(i)) != null)
|
2018-04-25 02:38:59 -04:00
|
|
|
|
.Select(c => (IModelConfiguration)Activator.CreateInstance(c));
|
2018-04-08 17:50:58 -04:00
|
|
|
|
|
|
|
|
|
foreach (var configurable in configurations)
|
2018-11-05 22:01:29 -05:00
|
|
|
|
{
|
2018-04-08 17:50:58 -04:00
|
|
|
|
configurable.Configure(modelBuilder);
|
2018-11-05 22:01:29 -05:00
|
|
|
|
}
|
2018-02-07 00:19:06 -05:00
|
|
|
|
|
2018-03-06 02:22:19 -05:00
|
|
|
|
foreach (var type in library.ExportedTypes)
|
2018-02-07 00:19:06 -05:00
|
|
|
|
{
|
|
|
|
|
if (type.IsClass && type.IsSubclassOf(typeof(SharedEntity)))
|
|
|
|
|
{
|
2018-04-08 17:50:58 -04:00
|
|
|
|
var method = modelBuilder.GetType().GetMethod("Entity", new[] { typeof(Type) });
|
|
|
|
|
method.Invoke(modelBuilder, new[] { type });
|
2018-02-07 00:19:06 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-25 20:29:58 -05:00
|
|
|
|
base.OnModelCreating(modelBuilder);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|