aboutsummaryrefslogtreecommitdiff
path: root/VNLib.Plugins.Extensions.Loading.Sql/SqlDbConnectionLoader.cs
blob: f0898a003dfa5df7de8c0d5e841535e64b4b8e23 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
using System;
using System.Text.Json;
using System.Data.Common;
using System.Runtime.CompilerServices;

using MySqlConnector;

using Microsoft.Data.Sqlite;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;

using VNLib.Utils.Logging;
using VNLib.Utils.Extensions;
using VNLib.Plugins.Extensions.Loading.Configuration;

namespace VNLib.Plugins.Extensions.Loading.Sql
{
    /// <summary>
    /// Provides common basic SQL loading extensions for plugins
    /// </summary>
    public static class SqlDbConnectionLoader
    {
        public const string SQL_CONFIG_KEY = "sql";
        public const string DB_PASSWORD_KEY = "db_password";

        private static readonly ConditionalWeakTable<PluginBase, Func<DbConnection>> LazyDbFuncTable = new();
        private static readonly ConditionalWeakTable<PluginBase, DbContextOptions> LazyCtxTable = new();
     

        /// <summary>
        /// Gets (or loads) the ambient sql connection factory for the current plugin
        /// </summary>
        /// <param name="plugin"></param>
        /// <returns>The ambient <see cref="DbConnection"/> factory</returns>
        /// <exception cref="KeyNotFoundException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        public static Func<DbConnection> GetConnectionFactory(this PluginBase plugin)
        {
            plugin.ThrowIfUnloaded();
            //Get or load
            return LazyDbFuncTable.GetValue(plugin, FactoryLoader);
        }

        private static Func<DbConnection> FactoryLoader(PluginBase plugin)
        {
            IReadOnlyDictionary<string, JsonElement>? sqlConf = plugin.TryGetConfig(SQL_CONFIG_KEY);
            
            if(sqlConf == null)
            {
                throw new KeyNotFoundException($"{SQL_CONFIG_KEY} configuration missing in configuration");
            }
            
            //Get the db-type
            string? type = sqlConf.GetPropString("db_type");
            string? password = plugin.TryGetSecretAsync(DB_PASSWORD_KEY).Result;

            if ("sqlite".Equals(type, StringComparison.OrdinalIgnoreCase))
            {
                //Use connection builder
                DbConnectionStringBuilder sqlBuilder = new SqliteConnectionStringBuilder()
                {
                    DataSource = sqlConf["source"].GetString(),
                };
                string connectionString = sqlBuilder.ToString();
                DbConnection DbFactory() => new SqliteConnection(connectionString);
                return DbFactory;
            }
            else if("mysql".Equals(type, StringComparison.OrdinalIgnoreCase))
            {
                DbConnectionStringBuilder sqlBuilder = new MySqlConnectionStringBuilder()
                {
                    Server = sqlConf["hostname"].GetString(),
                    Database = sqlConf["database"].GetString(),
                    UserID = sqlConf["username"].GetString(),
                    Password = password,
                    Pooling = true,
                    LoadBalance = MySqlLoadBalance.LeastConnections,
                    MinimumPoolSize = sqlConf["min_pool_size"].GetUInt32()
                };
                
                string connectionString = sqlBuilder.ToString();
                DbConnection DbFactory() => new MySqlConnection(connectionString);
                return DbFactory;
            }
            //Default to mssql
            else
            {
                //Use connection builder
                DbConnectionStringBuilder sqlBuilder = new SqlConnectionStringBuilder()
                {
                    DataSource = sqlConf["hostname"].GetString(),
                    UserID = sqlConf["username"].GetString(),
                    Password = password,
                    InitialCatalog = sqlConf["catalog"].GetString(),
                    IntegratedSecurity = sqlConf["ms_security"].GetBoolean(),
                    Pooling = true,
                    MinPoolSize = sqlConf["min_pool_size"].GetInt32(),
                    Replication = true
                };
                string connectionString = sqlBuilder.ToString();
                DbConnection DbFactory() => new SqlConnection(connectionString);
                return DbFactory;
            }           
        }

        /// <summary>
        /// Gets (or loads) the ambient <see cref="DbContextOptions"/> configured from 
        /// the ambient sql factory
        /// </summary>
        /// <param name="plugin"></param>
        /// <returns>The ambient <see cref="DbContextOptions"/> for the current plugin</returns>
        /// <exception cref="KeyNotFoundException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <remarks>If plugin is in debug mode, writes log data to the default log</remarks>
        public static DbContextOptions GetContextOptions(this PluginBase plugin)
        {
            plugin.ThrowIfUnloaded();
            return LazyCtxTable.GetValue(plugin, GetDbOptionsLoader);
        }

        private static DbContextOptions GetDbOptionsLoader(PluginBase plugin)
        {
            //Get a db connection object
            DbConnection connection = plugin.GetConnectionFactory().Invoke();
            DbContextOptionsBuilder builder = new();
            
            //Determine connection type
            if(connection is SqlConnection sql)
            {
                //Use sql server from connection
                builder.UseSqlServer(sql.ConnectionString);
            }
            else if(connection is SqliteConnection slc)
            {
                builder.UseSqlite(slc.ConnectionString);
            }
            else if(connection is MySqlConnection msconn)
            {
                //Detect version
                ServerVersion version = ServerVersion.AutoDetect(msconn);

                builder.UseMySql(msconn.ConnectionString, version);
            }
            
            //Enable logging
            if(plugin.IsDebug())
            {
                builder.LogTo(plugin.Log.Debug);
            }
            
            //Get context and freez it before returning
            DbContextOptions options = builder.Options;
            options.Freeze();
            return options;
        }
    }
}