From 842a8a2f718897ecf371de9592bfb9627cb4e366 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 9 Jul 2023 06:50:04 +0000 Subject: [PATCH] More work on figuring out the DB stuff and converting EF code to linqtodb where it's easy --- src/Nadeko.Bot.Common/DbService.cs | 74 ++---------------- .../Services/Currency/CurrencyService.cs | 5 +- .../Services/Currency/DefaultWallet.cs | 53 +++++++------ .../Services/Impl/BlacklistService.cs | 51 +++++++------ .../Impl/DiscordPermOverrideService.cs | 47 ++++++------ .../Extensions/DiscordUserExtensions.cs | 4 +- .../Extensions/GuildConfigExtensions.cs | 4 +- .../Extensions/UserXpExtensions.cs | 4 +- .../Extensions/WaifuExtensions.cs | 12 +-- .../Models}/CommandAlias.cs | 0 src/Nadeko.Bot.Db/NadekoBaseContext.cs | 7 -- .../Games/Polls}/PollExtensions.cs | 6 +- .../Games/Polls/PollRunner.cs | 2 +- .../Games/Polls/PollService.cs | 37 +++++---- .../Blacklist/BlacklistCommands.cs | 4 +- .../Nadeko.Bot.Modules.Utility.csproj | 4 + src/NadekoBot/Db/NadekoDbService.cs | 76 +++++++++++++++++++ 17 files changed, 210 insertions(+), 180 deletions(-) rename src/{Nadeko.Bot.Modules.Utility/_Common/Db => Nadeko.Bot.Db/Models}/CommandAlias.cs (100%) delete mode 100644 src/Nadeko.Bot.Db/NadekoBaseContext.cs rename src/{Nadeko.Bot.Db/Extensions => Nadeko.Bot.Modules.Gambling/Games/Polls}/PollExtensions.cs (76%) create mode 100644 src/NadekoBot/Db/NadekoDbService.cs diff --git a/src/Nadeko.Bot.Common/DbService.cs b/src/Nadeko.Bot.Common/DbService.cs index b829db2b6..c139c8f0c 100644 --- a/src/Nadeko.Bot.Common/DbService.cs +++ b/src/Nadeko.Bot.Common/DbService.cs @@ -6,73 +6,13 @@ using NadekoBot.Services.Database; namespace NadekoBot.Services; -public class DbService +public abstract class DbService { - private readonly IBotCredsProvider _creds; + /// + /// Call this to apply all migrations + /// + public abstract Task SetupAsync(); - // these are props because creds can change at runtime - private string DbType => _creds.GetCreds().Db.Type.ToLowerInvariant().Trim(); - private string ConnString => _creds.GetCreds().Db.ConnectionString; - - public DbService(IBotCredsProvider creds) - { - LinqToDBForEFTools.Initialize(); - Configuration.Linq.DisableQueryCache = true; - - _creds = creds; - } - - public async Task SetupAsync() - { - var dbType = DbType; - var connString = ConnString; - - await using var context = CreateRawDbContext(dbType, connString); - - // make sure sqlite db is in wal journal mode - if (context is SqliteContext) - { - await context.Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL"); - } - - await context.Database.MigrateAsync(); - } - - private static NadekoContext CreateRawDbContext(string dbType, string connString) - { - switch (dbType) - { - case "postgresql": - case "postgres": - case "pgsql": - return new PostgreSqlContext(connString); - case "mysql": - return new MysqlContext(connString); - case "sqlite": - return new SqliteContext(connString); - default: - throw new NotSupportedException($"The database provide type of '{dbType}' is not supported."); - } - } - - private NadekoContext GetDbContextInternal() - { - var dbType = DbType; - var connString = ConnString; - - var context = CreateRawDbContext(dbType, connString); - if (context is SqliteContext) - { - var conn = context.Database.GetDbConnection(); - conn.Open(); - using var com = conn.CreateCommand(); - com.CommandText = "PRAGMA synchronous=OFF"; - com.ExecuteNonQuery(); - } - - return context; - } - - public NadekoContext GetDbContext() - => GetDbContextInternal(); + public abstract DbContext CreateRawDbContext(string dbType, string connString); + public abstract DbContext GetDbContext(); } \ No newline at end of file diff --git a/src/Nadeko.Bot.Common/Services/Currency/CurrencyService.cs b/src/Nadeko.Bot.Common/Services/Currency/CurrencyService.cs index 6b1bd4975..fd2c697ca 100644 --- a/src/Nadeko.Bot.Common/Services/Currency/CurrencyService.cs +++ b/src/Nadeko.Bot.Common/Services/Currency/CurrencyService.cs @@ -1,5 +1,7 @@ #nullable disable using LinqToDB; +using LinqToDB.EntityFrameworkCore; +using NadekoBot.Db.Models; using NadekoBot.Services.Currency; namespace NadekoBot.Services; @@ -52,7 +54,8 @@ public sealed class CurrencyService : ICurrencyService, INService if (type == CurrencyType.Default) { await using var ctx = _db.GetDbContext(); - await ctx.DiscordUser + await ctx + .GetTable() .Where(x => userIds.Contains(x.UserId)) .UpdateAsync(du => new() { diff --git a/src/Nadeko.Bot.Common/Services/Currency/DefaultWallet.cs b/src/Nadeko.Bot.Common/Services/Currency/DefaultWallet.cs index 1bcc33121..93df5b96e 100644 --- a/src/Nadeko.Bot.Common/Services/Currency/DefaultWallet.cs +++ b/src/Nadeko.Bot.Common/Services/Currency/DefaultWallet.cs @@ -1,5 +1,6 @@ using LinqToDB; using LinqToDB.EntityFrameworkCore; +using NadekoBot.Db.Models; using NadekoBot.Services.Database.Models; namespace NadekoBot.Services.Currency; @@ -19,11 +20,11 @@ public class DefaultWallet : IWallet { await using var ctx = _db.GetDbContext(); var userId = UserId; - return await ctx.DiscordUser - .ToLinqToDBTable() - .Where(x => x.UserId == userId) - .Select(x => x.CurrencyAmount) - .FirstOrDefaultAsync(); + return await ctx + .GetTable() + .Where(x => x.UserId == userId) + .Select(x => x.CurrencyAmount) + .FirstOrDefaultAsync(); } public async Task Take(long amount, TxData? txData) @@ -34,12 +35,13 @@ public class DefaultWallet : IWallet await using var ctx = _db.GetDbContext(); var userId = UserId; - var changed = await ctx.DiscordUser - .Where(x => x.UserId == userId && x.CurrencyAmount >= amount) - .UpdateAsync(x => new() - { - CurrencyAmount = x.CurrencyAmount - amount - }); + var changed = await ctx + .GetTable() + .Where(x => x.UserId == userId && x.CurrencyAmount >= amount) + .UpdateAsync(x => new() + { + CurrencyAmount = x.CurrencyAmount - amount + }); if (changed == 0) return false; @@ -70,25 +72,26 @@ public class DefaultWallet : IWallet await using var ctx = _db.GetDbContext(); var userId = UserId; - + await using (var tran = await ctx.Database.BeginTransactionAsync()) { - var changed = await ctx.DiscordUser - .Where(x => x.UserId == userId) - .UpdateAsync(x => new() - { - CurrencyAmount = x.CurrencyAmount + amount - }); + var changed = await ctx + .GetTable() + .Where(x => x.UserId == userId) + .UpdateAsync(x => new() + { + CurrencyAmount = x.CurrencyAmount + amount + }); if (changed == 0) { - await ctx.DiscordUser - .ToLinqToDBTable() - .Value(x => x.UserId, userId) - .Value(x => x.Username, "Unknown") - .Value(x => x.Discriminator, "????") - .Value(x => x.CurrencyAmount, amount) - .InsertAsync(); + await ctx + .GetTable() + .Value(x => x.UserId, userId) + .Value(x => x.Username, "Unknown") + .Value(x => x.Discriminator, "????") + .Value(x => x.CurrencyAmount, amount) + .InsertAsync(); } await tran.CommitAsync(); diff --git a/src/Nadeko.Bot.Common/Services/Impl/BlacklistService.cs b/src/Nadeko.Bot.Common/Services/Impl/BlacklistService.cs index 2ffd393de..3f77f5fe0 100644 --- a/src/Nadeko.Bot.Common/Services/Impl/BlacklistService.cs +++ b/src/Nadeko.Bot.Common/Services/Impl/BlacklistService.cs @@ -1,7 +1,9 @@ #nullable disable -using Microsoft.EntityFrameworkCore; +using LinqToDB; +using LinqToDB.EntityFrameworkCore; using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Db; +using NadekoBot.Db.Models; using NadekoBot.Services.Database.Models; namespace NadekoBot.Modules.Permissions.Services; @@ -73,38 +75,36 @@ public sealed class BlacklistService : IExecOnMessage public void Reload(bool publish = true) { using var uow = _db.GetDbContext(); - var toPublish = uow.Blacklist.AsNoTracking().ToArray(); + var toPublish = uow.GetTable().ToArray(); blacklist = toPublish; if (publish) _pubSub.Pub(_blPubKey, toPublish); } - public void Blacklist(BlacklistType type, ulong id) + public async Task Blacklist(BlacklistType type, ulong id) { if (_creds.OwnerIds.Contains(id)) return; - using var uow = _db.GetDbContext(); - var item = new BlacklistEntry - { - ItemId = id, - Type = type - }; - uow.Blacklist.Add(item); - uow.SaveChanges(); + await using var uow = _db.GetDbContext(); + + await uow + .GetTable() + .InsertAsync(() => new() + { + ItemId = id, + Type = type, + }); Reload(); } - public void UnBlacklist(BlacklistType type, ulong id) + public async Task UnBlacklist(BlacklistType type, ulong id) { - using var uow = _db.GetDbContext(); - var toRemove = uow.Blacklist.FirstOrDefault(bi => bi.ItemId == id && bi.Type == type); - - if (toRemove is not null) - uow.Blacklist.Remove(toRemove); - - uow.SaveChanges(); + await using var uow = _db.GetDbContext(); + await uow.GetTable() + .Where(bi => bi.ItemId == id && bi.Type == type) + .DeleteAsync(); Reload(); } @@ -113,16 +113,21 @@ public sealed class BlacklistService : IExecOnMessage { using (var uow = _db.GetDbContext()) { - var bc = uow.Blacklist; - //blacklist the users + var bc = uow.Set(); bc.AddRange(toBlacklist.Select(x => new BlacklistEntry { ItemId = x, Type = BlacklistType.User })); - //clear their currencies - uow.DiscordUser.RemoveFromMany(toBlacklist); + // todo check if blacklist works and removes currency + uow.GetTable() + .UpdateAsync(x => toBlacklist.Contains(x.UserId), + _ => new() + { + CurrencyAmount = 0 + }); + uow.SaveChanges(); } diff --git a/src/Nadeko.Bot.Common/Services/Impl/DiscordPermOverrideService.cs b/src/Nadeko.Bot.Common/Services/Impl/DiscordPermOverrideService.cs index a513768b6..72c8f7223 100644 --- a/src/Nadeko.Bot.Common/Services/Impl/DiscordPermOverrideService.cs +++ b/src/Nadeko.Bot.Common/Services/Impl/DiscordPermOverrideService.cs @@ -18,10 +18,11 @@ public class DiscordPermOverrideService : INService, IExecPreCommand, IDiscordPe _db = db; _services = services; using var uow = _db.GetDbContext(); - _overrides = uow.DiscordPermOverrides.AsNoTracking() - .AsEnumerable() - .ToDictionary(o => (o.GuildId ?? 0, o.Command), o => o) - .ToConcurrent(); + _overrides = uow.Set() + .AsNoTracking() + .AsEnumerable() + .ToDictionary(o => (o.GuildId ?? 0, o.Command), o => o) + .ToConcurrent(); } public bool TryGetOverrides(ulong guildId, string commandName, out Nadeko.Bot.Db.GuildPerm? perm) @@ -52,18 +53,18 @@ public class DiscordPermOverrideService : INService, IExecPreCommand, IDiscordPe commandName = commandName.ToLowerInvariant(); await using var uow = _db.GetDbContext(); var over = await uow.Set() - .AsQueryable() - .FirstOrDefaultAsync(x => x.GuildId == guildId && commandName == x.Command); + .AsQueryable() + .FirstOrDefaultAsync(x => x.GuildId == guildId && commandName == x.Command); if (over is null) { uow.Set() - .Add(over = new() - { - Command = commandName, - Perm = (Nadeko.Bot.Db.GuildPerm)perm, - GuildId = guildId - }); + .Add(over = new() + { + Command = commandName, + Perm = (Nadeko.Bot.Db.GuildPerm)perm, + GuildId = guildId + }); } else over.Perm = (Nadeko.Bot.Db.GuildPerm)perm; @@ -77,10 +78,10 @@ public class DiscordPermOverrideService : INService, IExecPreCommand, IDiscordPe { await using var uow = _db.GetDbContext(); var overrides = await uow.Set() - .AsQueryable() - .AsNoTracking() - .Where(x => x.GuildId == guildId) - .ToListAsync(); + .AsQueryable() + .AsNoTracking() + .Where(x => x.GuildId == guildId) + .ToListAsync(); uow.RemoveRange(overrides); await uow.SaveChangesAsync(); @@ -95,9 +96,9 @@ public class DiscordPermOverrideService : INService, IExecPreCommand, IDiscordPe await using var uow = _db.GetDbContext(); var over = await uow.Set() - .AsQueryable() - .AsNoTracking() - .FirstOrDefaultAsync(x => x.GuildId == guildId && x.Command == commandName); + .AsQueryable() + .AsNoTracking() + .FirstOrDefaultAsync(x => x.GuildId == guildId && x.Command == commandName); if (over is null) return; @@ -112,10 +113,10 @@ public class DiscordPermOverrideService : INService, IExecPreCommand, IDiscordPe { await using var uow = _db.GetDbContext(); return await uow.Set() - .AsQueryable() - .AsNoTracking() - .Where(x => x.GuildId == guildId) - .ToListAsync(); + .AsQueryable() + .AsNoTracking() + .Where(x => x.GuildId == guildId) + .ToListAsync(); } public async Task ExecPreCommandAsync(ICommandContext context, string moduleName, CommandInfo command) diff --git a/src/Nadeko.Bot.Db/Extensions/DiscordUserExtensions.cs b/src/Nadeko.Bot.Db/Extensions/DiscordUserExtensions.cs index cb2ecc98c..3f15e189a 100644 --- a/src/Nadeko.Bot.Db/Extensions/DiscordUserExtensions.cs +++ b/src/Nadeko.Bot.Db/Extensions/DiscordUserExtensions.cs @@ -42,7 +42,7 @@ public static class DiscordUserExtensions }); public static Task EnsureUserCreatedAsync( - this NadekoBaseContext ctx, + this DbContext ctx, ulong userId) => ctx.GetTable() .InsertOrUpdateAsync( @@ -66,7 +66,7 @@ public static class DiscordUserExtensions //temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown public static DiscordUser GetOrCreateUser( - this NadekoBaseContext ctx, + this DbContext ctx, ulong userId, string username, string discrim, diff --git a/src/Nadeko.Bot.Db/Extensions/GuildConfigExtensions.cs b/src/Nadeko.Bot.Db/Extensions/GuildConfigExtensions.cs index df55da37d..dea053769 100644 --- a/src/Nadeko.Bot.Db/Extensions/GuildConfigExtensions.cs +++ b/src/Nadeko.Bot.Db/Extensions/GuildConfigExtensions.cs @@ -65,7 +65,7 @@ public static class GuildConfigExtensions /// Use to manipulate the set however you want. Pass null to include everything /// Config for the guild public static GuildConfig GuildConfigsForId( - this NadekoBaseContext ctx, + this DbContext ctx, ulong guildId, Func, IQueryable> includes) { @@ -193,7 +193,7 @@ public static class GuildConfigExtensions conf.CleverbotEnabled = cleverbotEnabled; } - public static XpSettings XpSettingsFor(this NadekoBaseContext ctx, ulong guildId) + public static XpSettings XpSettingsFor(this DbContext ctx, ulong guildId) { var gc = ctx.GuildConfigsForId(guildId, set => set.Include(x => x.XpSettings) diff --git a/src/Nadeko.Bot.Db/Extensions/UserXpExtensions.cs b/src/Nadeko.Bot.Db/Extensions/UserXpExtensions.cs index 5308f83d2..3214a03b0 100644 --- a/src/Nadeko.Bot.Db/Extensions/UserXpExtensions.cs +++ b/src/Nadeko.Bot.Db/Extensions/UserXpExtensions.cs @@ -9,9 +9,9 @@ namespace NadekoBot.Db; public static class UserXpExtensions { - public static UserXpStats GetOrCreateUserXpStats(this NadekoContext ctx, ulong guildId, ulong userId) + public static UserXpStats GetOrCreateUserXpStats(this DbContext ctx, ulong guildId, ulong userId) { - var usr = ctx.UserXpStats.FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId); + var usr = ctx.Set().FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId); if (usr is null) { diff --git a/src/Nadeko.Bot.Db/Extensions/WaifuExtensions.cs b/src/Nadeko.Bot.Db/Extensions/WaifuExtensions.cs index a5487f3a8..0abb578a4 100644 --- a/src/Nadeko.Bot.Db/Extensions/WaifuExtensions.cs +++ b/src/Nadeko.Bot.Db/Extensions/WaifuExtensions.cs @@ -75,24 +75,24 @@ public static class WaifuExtensions .Select(x => x.Waifu.UserId) .FirstOrDefault(); - public static async Task GetWaifuInfoAsync(this NadekoContext ctx, ulong userId) + public static async Task GetWaifuInfoAsync(this DbContext ctx, ulong userId) { - await ctx.WaifuInfo + await ctx.Set() .ToLinqToDBTable() .InsertOrUpdateAsync(() => new() { AffinityId = null, ClaimerId = null, Price = 1, - WaifuId = ctx.DiscordUser.Where(x => x.UserId == userId).Select(x => x.Id).First() + WaifuId = ctx.Set().Where(x => x.UserId == userId).Select(x => x.Id).First() }, _ => new(), () => new() { - WaifuId = ctx.DiscordUser.Where(x => x.UserId == userId).Select(x => x.Id).First() + WaifuId = ctx.Set().Where(x => x.UserId == userId).Select(x => x.Id).First() }); - var toReturn = ctx.WaifuInfo.AsQueryable() + var toReturn = ctx.Set().AsQueryable() .Where(w => w.WaifuId == ctx.Set() .AsQueryable() @@ -120,7 +120,7 @@ public static class WaifuExtensions .Where(u => u.Id == w.AffinityId) .Select(u => u.Username + "#" + u.Discriminator) .FirstOrDefault(), - ClaimCount = ctx.WaifuInfo.AsQueryable().Count(x => x.ClaimerId == w.WaifuId), + ClaimCount = ctx.Set().AsQueryable().Count(x => x.ClaimerId == w.WaifuId), ClaimerName = ctx.Set() .AsQueryable() diff --git a/src/Nadeko.Bot.Modules.Utility/_Common/Db/CommandAlias.cs b/src/Nadeko.Bot.Db/Models/CommandAlias.cs similarity index 100% rename from src/Nadeko.Bot.Modules.Utility/_Common/Db/CommandAlias.cs rename to src/Nadeko.Bot.Db/Models/CommandAlias.cs diff --git a/src/Nadeko.Bot.Db/NadekoBaseContext.cs b/src/Nadeko.Bot.Db/NadekoBaseContext.cs deleted file mode 100644 index 04c3b484a..000000000 --- a/src/Nadeko.Bot.Db/NadekoBaseContext.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace NadekoBot.Db; - -public abstract class NadekoBaseContext : DbContext -{ -} \ No newline at end of file diff --git a/src/Nadeko.Bot.Db/Extensions/PollExtensions.cs b/src/Nadeko.Bot.Modules.Gambling/Games/Polls/PollExtensions.cs similarity index 76% rename from src/Nadeko.Bot.Db/Extensions/PollExtensions.cs rename to src/Nadeko.Bot.Modules.Gambling/Games/Polls/PollExtensions.cs index 01be1e3d6..40f7abedc 100644 --- a/src/Nadeko.Bot.Db/Extensions/PollExtensions.cs +++ b/src/Nadeko.Bot.Modules.Gambling/Games/Polls/PollExtensions.cs @@ -12,9 +12,9 @@ public static class PollExtensions .Include(x => x.Votes) .ToArray(); - public static void RemovePoll(this NadekoContext ctx, int id) + public static void RemovePoll(this DbContext ctx, int id) { - var p = ctx.Poll.Include(x => x.Answers).Include(x => x.Votes).FirstOrDefault(x => x.Id == id); + var p = ctx.Set().Include(x => x.Answers).Include(x => x.Votes).FirstOrDefault(x => x.Id == id); if (p is null) return; @@ -31,6 +31,6 @@ public static class PollExtensions p.Answers.Clear(); } - ctx.Poll.Remove(p); + ctx.Set().Remove(p); } } \ No newline at end of file diff --git a/src/Nadeko.Bot.Modules.Gambling/Games/Polls/PollRunner.cs b/src/Nadeko.Bot.Modules.Gambling/Games/Polls/PollRunner.cs index 730a981be..618efd487 100644 --- a/src/Nadeko.Bot.Modules.Gambling/Games/Polls/PollRunner.cs +++ b/src/Nadeko.Bot.Modules.Gambling/Games/Polls/PollRunner.cs @@ -52,7 +52,7 @@ public class PollRunner finally { _locker.Release(); } await using var uow = _db.GetDbContext(); - var trackedPoll = uow.Poll.FirstOrDefault(x => x.Id == Poll.Id); + var trackedPoll = uow.Set().FirstOrDefault(x => x.Id == Poll.Id); trackedPoll.Votes.Add(voteObj); uow.SaveChanges(); return true; diff --git a/src/Nadeko.Bot.Modules.Gambling/Games/Polls/PollService.cs b/src/Nadeko.Bot.Modules.Gambling/Games/Polls/PollService.cs index 1563710fc..efae39526 100644 --- a/src/Nadeko.Bot.Modules.Gambling/Games/Polls/PollService.cs +++ b/src/Nadeko.Bot.Modules.Gambling/Games/Polls/PollService.cs @@ -24,15 +24,15 @@ public class PollService : IExecOnMessage _eb = eb; using var uow = db.GetDbContext(); - ActivePolls = uow.Poll.GetAllPolls() - .ToDictionary(x => x.GuildId, - x => - { - var pr = new PollRunner(db, x); - pr.OnVoted += Pr_OnVoted; - return pr; - }) - .ToConcurrent(); + ActivePolls = uow.Set().GetAllPolls() + .ToDictionary(x => x.GuildId, + x => + { + var pr = new PollRunner(db, x); + pr.OnVoted += Pr_OnVoted; + return pr; + }) + .ToConcurrent(); } public Poll CreatePoll(ulong guildId, ulong channelId, string input) @@ -44,10 +44,10 @@ public class PollService : IExecOnMessage return null; var col = new IndexedCollection(data.Skip(1) - .Select(x => new PollAnswer - { - Text = x - })); + .Select(x => new PollAnswer + { + Text = x + })); return new() { @@ -66,7 +66,7 @@ public class PollService : IExecOnMessage { using (var uow = _db.GetDbContext()) { - uow.Poll.Add(p); + uow.Set().Add(p); uow.SaveChanges(); } @@ -98,8 +98,13 @@ public class PollService : IExecOnMessage var toDelete = await msg.Channel.SendConfirmAsync(_eb, _strs.GetText(strs.poll_voted(Format.Bold(usr.ToString())), usr.GuildId)); toDelete.DeleteAfter(5); - try { await msg.DeleteAsync(); } - catch { } + try + { + await msg.DeleteAsync(); + } + catch + { + } } public async Task ExecOnMessageAsync(IGuild guild, IUserMessage msg) diff --git a/src/Nadeko.Bot.Modules.Permisssions/Blacklist/BlacklistCommands.cs b/src/Nadeko.Bot.Modules.Permisssions/Blacklist/BlacklistCommands.cs index e51041a3e..025753b91 100644 --- a/src/Nadeko.Bot.Modules.Permisssions/Blacklist/BlacklistCommands.cs +++ b/src/Nadeko.Bot.Modules.Permisssions/Blacklist/BlacklistCommands.cs @@ -124,9 +124,9 @@ public partial class Permissions private async Task Blacklist(AddRemove action, ulong id, BlacklistType type) { if (action == AddRemove.Add) - _service.Blacklist(type, id); + await _service.Blacklist(type, id); else - _service.UnBlacklist(type, id); + await _service.UnBlacklist(type, id); if (action == AddRemove.Add) { diff --git a/src/Nadeko.Bot.Modules.Utility/Nadeko.Bot.Modules.Utility.csproj b/src/Nadeko.Bot.Modules.Utility/Nadeko.Bot.Modules.Utility.csproj index 919f7ceab..590ef6b35 100644 --- a/src/Nadeko.Bot.Modules.Utility/Nadeko.Bot.Modules.Utility.csproj +++ b/src/Nadeko.Bot.Modules.Utility/Nadeko.Bot.Modules.Utility.csproj @@ -24,4 +24,8 @@ + + + + diff --git a/src/NadekoBot/Db/NadekoDbService.cs b/src/NadekoBot/Db/NadekoDbService.cs new file mode 100644 index 000000000..0671eff07 --- /dev/null +++ b/src/NadekoBot/Db/NadekoDbService.cs @@ -0,0 +1,76 @@ +using LinqToDB.Common; +using LinqToDB.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +namespace NadekoBot.Services.Database; + +public sealed class NadekoDbService : DbService +{ + private readonly IBotCredsProvider _creds; + + // these are props because creds can change at runtime + private string DbType => _creds.GetCreds().Db.Type.ToLowerInvariant().Trim(); + private string ConnString => _creds.GetCreds().Db.ConnectionString; + + public NadekoDbService(IBotCredsProvider creds) + { + LinqToDBForEFTools.Initialize(); + Configuration.Linq.DisableQueryCache = true; + + _creds = creds; + } + + public override async Task SetupAsync() + { + var dbType = DbType; + var connString = ConnString; + + await using var context = CreateRawDbContext(dbType, connString); + + // make sure sqlite db is in wal journal mode + if (context is SqliteContext) + { + await context.Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL"); + } + + await context.Database.MigrateAsync(); + } + + public override NadekoContext CreateRawDbContext(string dbType, string connString) + { + switch (dbType) + { + case "postgresql": + case "postgres": + case "pgsql": + return new PostgreSqlContext(connString); + case "mysql": + return new MysqlContext(connString); + case "sqlite": + return new SqliteContext(connString); + default: + throw new NotSupportedException($"The database provide type of '{dbType}' is not supported."); + } + } + + private NadekoContext GetDbContextInternal() + { + var dbType = DbType; + var connString = ConnString; + + var context = CreateRawDbContext(dbType, connString); + if (context is SqliteContext) + { + var conn = context.Database.GetDbConnection(); + conn.Open(); + using var com = conn.CreateCommand(); + com.CommandText = "PRAGMA synchronous=OFF"; + com.ExecuteNonQuery(); + } + + return context; + } + + public override NadekoContext GetDbContext() + => GetDbContextInternal(); +} \ No newline at end of file