Unity 2017.2+C#数据库类动态切换实现方案咨询
实现思路与代码示例
嘿,这个需求其实是策略模式+单例/依赖注入的经典应用场景,刚好能完美解决你这种多数据库动态切换的需求,我给你一步步拆解具体实现:
1. 确保抽象类是统一契约
首先要保证你的DatabaseTemplate抽象类定义了所有需要对外暴露的方法,作为两个派生类的统一接口:
public abstract class DatabaseTemplate { // 定义你需要的所有抽象方法,比如QueryScalar、ExecuteNonQuery等 public abstract object QueryScalar(string sql); public abstract int ExecuteNonQuery(string sql); // 其他数据库操作方法... }
2. 完善派生类的实现
你的BaseMysql和BaseSQlite只需要正确继承抽象类,并实现所有抽象方法即可(注意处理各自的数据库连接和类库依赖):
// MySQL实现类 public class BaseMysql : DatabaseTemplate { private MySqlConnection _dbConnection; // 构造函数初始化连接 public BaseMysql(string connectionString) { _dbConnection = new MySqlConnection(connectionString); _dbConnection.Open(); } public override object QueryScalar(string sql) { using (var cmd = new MySqlCommand(sql, _dbConnection)) { return cmd.ExecuteScalar(); } } public override int ExecuteNonQuery(string sql) { using (var cmd = new MySqlCommand(sql, _dbConnection)) { return cmd.ExecuteNonQuery(); } } // 其他方法实现... } // SQLite实现类 public class BaseSQlite : DatabaseTemplate { private SQLiteConnection _dbConnection; public BaseSQlite(string connectionString) { _dbConnection = new SQLiteConnection(connectionString); _dbConnection.Open(); } public override object QueryScalar(string sql) { using (var cmd = _dbConnection.CreateCommand()) { cmd.CommandText = sql; return cmd.ExecuteScalar(); } } public override int ExecuteNonQuery(string sql) { using (var cmd = _dbConnection.CreateCommand()) { cmd.CommandText = sql; return cmd.ExecuteNonQuery(); } } // 其他方法实现... }
3. 实现统一的DatabaseManager
这个类是核心,负责初始化具体数据库实例,并对外暴露统一的调用入口。考虑到Unity的场景,用单例模式最合适:
public class DatabaseManager : MonoBehaviour { private DatabaseTemplate _currentDbInstance; // 全局单例实例,确保整个游戏只有一个数据库管理器 public static DatabaseManager Instance { get; private set; } private void Awake() { // 单例逻辑,避免重复创建 if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; DontDestroyOnLoad(gameObject); // 这里可以从配置文件、PlayerPrefs或Inspector读取数据库类型 // 示例:默认用SQLite,可通过PlayerPrefs切换到MySQL string dbType = PlayerPrefs.GetString("ActiveDatabase", "SQLite"); string connectionStr = GetConnectionString(dbType); // 初始化对应数据库实例 InitializeDatabase(dbType, connectionStr); } // 根据数据库类型返回对应的连接字符串 private string GetConnectionString(string dbType) { return dbType switch { "MySQL" => "server=localhost;user=root;password=your_pwd;database=your_db", "SQLite" => "URI=file:game_db.db", _ => throw new ArgumentException($"不支持的数据库类型:{dbType}") }; } // 初始化具体数据库实例 private void InitializeDatabase(string dbType, string connectionStr) { _currentDbInstance = dbType switch { "MySQL" => new BaseMysql(connectionStr), "SQLite" => new BaseSQlite(connectionStr), _ => throw new ArgumentException($"无法初始化数据库:{dbType}") }; } // 对外暴露的统一方法,直接转发给当前数据库实例 public object QueryScalar(string sql) { ValidateDbInstance(); return _currentDbInstance.QueryScalar(sql); } public int ExecuteNonQuery(string sql) { ValidateDbInstance(); return _currentDbInstance.ExecuteNonQuery(sql); } // 辅助方法:检查数据库实例是否已初始化 private void ValidateDbInstance() { if (_currentDbInstance == null) { throw new InvalidOperationException("数据库实例未初始化,请检查配置"); } } }
4. 使用方式
在Unity中,把DatabaseManager脚本挂在一个空物体上(比如命名为"DatabaseManager"),然后在任何需要调用数据库的地方直接使用:
// 示例:获取用户总数 void GetTotalUsers() { try { object userCount = DatabaseManager.Instance.QueryScalar("SELECT count(*) FROM users"); Debug.Log($"当前用户总数:{userCount}"); } catch (Exception ex) { Debug.LogError($"查询用户总数失败:{ex.Message}"); } }
额外优化建议
- 把数据库配置(类型、连接字符串)做成ScriptableObject,这样可以直接在Unity Inspector里修改,不用改代码;
- 增加连接池管理和异常统一处理,提升稳定性;
- 如果后续需要支持更多数据库,只需要新增一个继承
DatabaseTemplate的派生类,再在DatabaseManager的初始化逻辑里加一个case即可,完全符合开闭原则。
内容的提问来源于stack exchange,提问作者Maikel Urlitzki
相关产品推荐
相关产品推荐

