第九章:高级特性与扩展
9.1 多数据库支持
9.1.1 支持的数据库列表
SOD框架支持多种数据库,包括内置支持和扩展支持:
内置数据库提供程序(PWMIS.Core.dll):
| 数据库 | ProviderName | 说明 |
|---|---|---|
| SQL Server | SqlServer | 微软SQL Server数据库 |
| Oracle | Oracle | Oracle数据库(使用ODP.NET) |
| Access | Access | Microsoft Access数据库 |
| SQL CE | SqlCe | SQL Server Compact Edition |
| OleDb | OleDb | 通用OLE DB驱动 |
| ODBC | Odbc | 通用ODBC驱动 |
扩展数据库提供程序:
| 数据库 | NuGet包 | ProviderName |
|---|---|---|
| MySQL | PDF.NET.SOD.MySQL.Provider | PWMIS.DataProvider.Data.MySQL,PWMIS.MySqlClient |
| PostgreSQL | PDF.NET.SOD.PostgreSQL.Provider | PWMIS.DataProvider.Data.PostgreSQL,PWMIS.PostgreSQLClient |
| SQLite | PDF.NET.SOD.SQLite.Provider | PWMIS.DataProvider.Data.SQLite,PWMIS.SQLiteClient |
| 达梦 | PWMIS.SOD.DaMeng.Provider | PWMIS.DataProvider.Data.Dameng,PWMIS.DamengClient |
| 人大金仓 | PWMIS.SOD.Kingbase.Provider | PWMIS.DataProvider.Data.Kingbase,PWMIS.KingbaseClient |
9.1.2 配置多数据库连接
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<!-- SQL Server -->
<add name="sqlserver"
connectionString="Data Source=.;Initial Catalog=TestDB;Integrated Security=True"
providerName="SqlServer"/>
<!-- MySQL -->
<add name="mysql"
connectionString="Server=localhost;Database=testdb;Uid=root;Pwd=123456;"
providerName="PWMIS.DataProvider.Data.MySQL,PWMIS.MySqlClient"/>
<!-- PostgreSQL -->
<add name="postgresql"
connectionString="Host=localhost;Database=testdb;Username=postgres;Password=123456;"
providerName="PWMIS.DataProvider.Data.PostgreSQL,PWMIS.PostgreSQLClient"/>
<!-- SQLite -->
<add name="sqlite"
connectionString="Data Source=./testdb.db;Version=3;"
providerName="PWMIS.DataProvider.Data.SQLite,PWMIS.SQLiteClient"/>
<!-- 达梦 -->
<add name="dameng"
connectionString="Server=localhost;Database=DMTEST;User Id=SYSDBA;PWD=SYSDBA;"
providerName="PWMIS.DataProvider.Data.Dameng,PWMIS.DamengClient"/>
<!-- 人大金仓 -->
<add name="kingbase"
connectionString="Host=localhost;Database=testdb;Username=SYSTEM;Password=123456;"
providerName="PWMIS.DataProvider.Data.Kingbase,PWMIS.KingbaseClient"/>
</connectionStrings>
</configuration>
9.1.3 代码中切换数据库
// 根据连接名获取不同数据库
AdoHelper sqlServerDb = MyDB.GetDBHelperByConnectionName("sqlserver");
AdoHelper mysqlDb = MyDB.GetDBHelperByConnectionName("mysql");
AdoHelper pgsqlDb = MyDB.GetDBHelperByConnectionName("postgresql");
// 同一套代码,不同数据库执行
UserEntity user = new UserEntity { Name = "测试用户" };
// SQL Server
var oql1 = OQL.From(user).Select().Where(user.Name).END;
var users1 = EntityQuery<UserEntity>.QueryList(oql1, sqlServerDb);
// MySQL
var oql2 = OQL.From(user).Select().Where(user.Name).END;
var users2 = EntityQuery<UserEntity>.QueryList(oql2, mysqlDb);
9.1.4 数据库差异处理
不同数据库在SQL语法上有差异,SOD框架会自动处理:
// 分页查询 - 框架自动适配不同数据库语法
var oql = OQL.From(user).Select().OrderBy(user.ID).END;
oql.Limit(10, 1);
// SQL Server: SELECT TOP 10 ... 或 OFFSET ... FETCH
// MySQL: SELECT ... LIMIT 10 OFFSET 0
// Oracle: SELECT ... WHERE ROWNUM <= 10 或使用ROW_NUMBER()
// PostgreSQL: SELECT ... LIMIT 10 OFFSET 0
var users = EntityQuery<UserEntity>.QueryList(oql, db);
9.2 自定义数据提供程序
9.2.1 创建自定义提供程序
using PWMIS.DataProvider.Data;
using System.Data;
using System.Data.Common;
namespace MyApp.DataProviders
{
/// <summary>
/// 自定义数据库提供程序
/// </summary>
public class MyCustomProvider : AdoHelper
{
private readonly DbProviderFactory _factory;
public MyCustomProvider()
{
// 获取底层DbProviderFactory
_factory = GetDbProviderFactory();
}
protected virtual DbProviderFactory GetDbProviderFactory()
{
// 返回具体的数据库工厂
return DbProviderFactories.GetFactory("MyCustomProvider");
}
public override string ConnectionString { get; set; }
public override IDbConnection GetConnection()
{
var conn = _factory.CreateConnection();
conn.ConnectionString = this.ConnectionString;
return conn;
}
public override IDbCommand GetCommand()
{
return _factory.CreateCommand();
}
public override IDbDataAdapter GetDataAdapter()
{
return _factory.CreateDataAdapter();
}
public override IDataParameter GetParameter(string parameterName, object value)
{
var param = _factory.CreateParameter();
param.ParameterName = GetParameterPrefix() + parameterName;
param.Value = value ?? DBNull.Value;
return param;
}
// 获取参数前缀
public override string GetParameterPrefix()
{
return "@"; // 根据数据库类型调整
}
// 获取SQL模板
protected override string GetSelectSqlTemplate()
{
return "SELECT {0} FROM {1} {2}";
}
// 获取分页SQL
public override string GetPagedSql(string sql, int pageSize, int pageIndex)
{
int offset = (pageIndex - 1) * pageSize;
return $"{sql} LIMIT {pageSize} OFFSET {offset}";
}
// 获取自增ID的SQL
public override string GetIdentitySql()
{
return "SELECT LAST_INSERT_ID()";
}
}
}
9.2.2 注册自定义提供程序
<configuration>
<connectionStrings>
<add name="custom"
connectionString="your connection string"
providerName="MyApp.DataProviders.MyCustomProvider,MyApp"/>
</connectionStrings>
</configuration>
9.2.3 使用自定义提供程序
// 通过配置使用
AdoHelper db = MyDB.GetDBHelperByConnectionName("custom");
// 直接实例化
var db = new MyCustomProvider();
db.ConnectionString = "your connection string";
// 正常使用
var users = db.QueryList<UserEntity>("SELECT * FROM Users");
9.3 查询日志与调试
9.3.1 启用查询日志
using PWMIS.DataProvider.Data;
// 启用查询日志
CommandLog.Instance.Enabled = true;
// 设置日志文件路径
CommandLog.Instance.LogFile = "sql_log.txt";
// 设置日志级别
CommandLog.Instance.LogLevel = LogLevel.All; // Info, Warning, Error, All
// 执行查询后会自动记录日志
var users = EntityQuery<UserEntity>.QueryList(oql);
9.3.2 自定义日志处理
// 订阅日志事件
CommandLog.Instance.OnLog += (sender, args) =>
{
Console.WriteLine($"[{args.Time:HH:mm:ss}] {args.Level}: {args.Message}");
Console.WriteLine($"SQL: {args.CommandText}");
Console.WriteLine($"Parameters: {args.Parameters}");
Console.WriteLine($"Duration: {args.Duration}ms");
};
9.3.3 调试OQL
UserEntity user = new UserEntity();
user.Name = "张三";
var oql = OQL.From(user)
.Select(user.ID, user.Name, user.Email)
.Where(user.Name)
.OrderBy(user.ID)
.END;
// 输出生成的SQL
Console.WriteLine("生成的SQL:");
Console.WriteLine(oql.ToString());
// 输出参数信息
Console.WriteLine("\n参数信息:");
Console.WriteLine(oql.PrintParameterInfo());
// 输出示例:
// 生成的SQL:
// SELECT ID, Name, Email FROM TbUser WHERE Name = @P0 ORDER BY ID
//
// 参数信息:
// --------OQL Parameters information----------
// have 1 parameter,detail:
// @P0=张三 Type:String
// ------------------End------------------------
9.4 序列化与反序列化
9.4.1 二进制序列化
using PWMIS.Common;
// 序列化实体对象
UserEntity user = new UserEntity
{
ID = 1,
Name = "张三",
Email = "zhangsan@example.com"
};
// 序列化为字节数组
byte[] data = BinarySerializer.Serialize(user);
// 反序列化
UserEntity deserializedUser = BinarySerializer.Deserialize<UserEntity>(data);
9.4.2 JSON序列化
using System.Text.Json;
// 实体类需要可被JSON序列化
public class UserDto
{
public int ID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
// 从实体类转换
UserEntity entity = new UserEntity();
// ... 查询数据
var dto = new UserDto
{
ID = entity.ID,
Name = entity.Name,
Email = entity.Email
};
// JSON序列化
string json = JsonSerializer.Serialize(dto);
// JSON反序列化
UserDto deserializedDto = JsonSerializer.Deserialize<UserDto>(json);
9.4.3 实体类序列化扩展
public static class EntityExtensions
{
// 转换为字典
public static Dictionary<string, object> ToDictionary(this EntityBase entity)
{
var dict = new Dictionary<string, object>();
foreach (var propName in entity.PropertyNames)
{
dict[propName] = entity[propName];
}
return dict;
}
// 从字典加载
public static void LoadFromDictionary(this EntityBase entity, Dictionary<string, object> dict)
{
foreach (var kvp in dict)
{
if (entity.PropertyNames.Contains(kvp.Key))
{
entity[kvp.Key] = kvp.Value;
}
}
}
// 转换为JSON
public static string ToJson(this EntityBase entity)
{
return JsonSerializer.Serialize(entity.ToDictionary());
}
}
9.5 存储过程调用
9.5.1 无参数存储过程
AdoHelper db = MyDB.GetDBHelper();
// 调用存储过程
DataSet ds = db.ExecuteDataSet("sp_GetAllUsers", CommandType.StoredProcedure, null);
DataTable dt = ds.Tables[0];
9.5.2 带参数存储过程
AdoHelper db = MyDB.GetDBHelper();
// 定义参数
IDataParameter[] parameters = new IDataParameter[]
{
db.GetParameter("@DeptId", 1),
db.GetParameter("@Status", 1)
};
// 执行存储过程
DataSet ds = db.ExecuteDataSet("sp_GetUsersByDept", CommandType.StoredProcedure, parameters);
9.5.3 带输出参数的存储过程
AdoHelper db = MyDB.GetDBHelper();
// 定义输入参数
var inputParam = db.GetParameter("@DeptId", 1);
// 定义输出参数
var outputParam = db.GetParameter("@TotalCount", null);
outputParam.Direction = ParameterDirection.Output;
outputParam.DbType = DbType.Int32;
IDataParameter[] parameters = new IDataParameter[]
{
inputParam,
outputParam
};
// 执行存储过程
db.ExecuteNonQuery("sp_GetUserCount", CommandType.StoredProcedure, parameters);
// 获取输出参数值
int totalCount = Convert.ToInt32(outputParam.Value);
Console.WriteLine($"用户总数: {totalCount}");
9.5.4 返回结果集的存储过程
// SQL Server存储过程示例
/*
CREATE PROCEDURE sp_GetUsersByDept
@DeptId INT,
@Status INT
AS
BEGIN
SELECT * FROM TbUser WHERE DeptId = @DeptId AND Status = @Status
END
*/
AdoHelper db = MyDB.GetDBHelper();
var parameters = new IDataParameter[]
{
db.GetParameter("@DeptId", 1),
db.GetParameter("@Status", 1)
};
// 获取DataSet
DataSet ds = db.ExecuteDataSet("sp_GetUsersByDept", CommandType.StoredProcedure, parameters);
// 映射到实体列表
List<UserEntity> users = new List<UserEntity>();
foreach (DataRow row in ds.Tables[0].Rows)
{
var user = new UserEntity();
user["ID"] = row["ID"];
user["Name"] = row["Name"];
user["Email"] = row["Email"];
users.Add(user);
}
9.6 动态SQL
9.6.1 SQL构建器
public class SqlBuilder
{
private StringBuilder _sql = new StringBuilder();
private List<IDataParameter> _parameters = new List<IDataParameter>();
private AdoHelper _db;
private int _paramIndex = 0;
public SqlBuilder(AdoHelper db)
{
_db = db;
}
public SqlBuilder Select(params string[] columns)
{
_sql.Append("SELECT ");
_sql.Append(columns.Length > 0 ? string.Join(", ", columns) : "*");
return this;
}
public SqlBuilder From(string table)
{
_sql.Append($" FROM {table}");
return this;
}
public SqlBuilder Where(string condition, object value)
{
string paramName = $"@P{_paramIndex++}";
_sql.Append(_parameters.Count == 0 ? " WHERE " : " AND ");
_sql.Append(condition.Replace("?", paramName));
_parameters.Add(_db.GetParameter(paramName, value));
return this;
}
public SqlBuilder WhereIf(bool condition, string sqlCondition, object value)
{
if (condition)
{
Where(sqlCondition, value);
}
return this;
}
public SqlBuilder OrderBy(string column, bool desc = false)
{
_sql.Append($" ORDER BY {column}");
if (desc) _sql.Append(" DESC");
return this;
}
public string ToSql()
{
return _sql.ToString();
}
public IDataParameter[] GetParameters()
{
return _parameters.ToArray();
}
public DataSet Execute()
{
return _db.ExecuteDataSet(ToSql(), CommandType.Text, GetParameters());
}
public List<T> QueryList<T>() where T : new()
{
return _db.QueryList<T>(ToSql(), GetParameters());
}
}
// 使用示例
AdoHelper db = MyDB.GetDBHelper();
string name = "张";
int? status = 1;
var result = new SqlBuilder(db)
.Select("ID", "Name", "Email", "Status")
.From("TbUser")
.WhereIf(!string.IsNullOrEmpty(name), "Name LIKE ?", $"%{name}%")
.WhereIf(status.HasValue, "Status = ?", status.Value)
.OrderBy("ID", desc: true)
.QueryList<UserEntity>();
9.7 性能优化技巧
9.7.1 连接池配置
<!-- SQL Server连接池配置 -->
<add name="sqlserver"
connectionString="Data Source=.;Initial Catalog=TestDB;Integrated Security=True;
Min Pool Size=5;Max Pool Size=100;Connection Lifetime=0;"
providerName="SqlServer"/>
9.7.2 批量操作优化
// 不推荐:循环单条插入
foreach (var user in users)
{
EntityQuery<UserEntity>.Instance.Insert(user, db);
}
// 推荐:使用事务批量插入
db.OpenSession();
db.BeginTransaction();
try
{
foreach (var user in users)
{
EntityQuery<UserEntity>.Instance.Insert(user, db);
}
db.Commit();
}
catch
{
db.Rollback();
throw;
}
finally
{
db.CloseSession();
}
9.7.3 查询优化
// 只查询需要的字段
var oql = OQL.From(user)
.Select(user.ID, user.Name) // 不要 Select() 全选
.Where(user.Status)
.END;
// 使用分页避免大量数据
oql.Limit(100, 1);
// 利用索引字段作为查询条件
// 确保 Status 字段有索引
9.7.4 缓存策略
public class CachedRepository<T> where T : EntityBase, new()
{
private readonly MemoryCache _cache = MemoryCache.Default;
private readonly AdoHelper _db;
private readonly string _cacheKeyPrefix;
public CachedRepository(AdoHelper db)
{
_db = db;
_cacheKeyPrefix = typeof(T).Name;
}
public T GetById(int id, TimeSpan? expiration = null)
{
string cacheKey = $"{_cacheKeyPrefix}_{id}";
// 尝试从缓存获取
var cached = _cache.Get(cacheKey) as T;
if (cached != null)
{
return cached;
}
// 缓存未命中,查询数据库
var entity = new T();
entity["ID"] = id;
var oql = OQL.From(entity).Select().Where(entity["ID"]).END;
var result = EntityQuery<T>.QueryObject(oql, _db);
// 放入缓存
if (result != null)
{
var policy = new CacheItemPolicy
{
AbsoluteExpiration = DateTimeOffset.Now.Add(expiration ?? TimeSpan.FromMinutes(10))
};
_cache.Set(cacheKey, result, policy);
}
return result;
}
public void InvalidateCache(int id)
{
string cacheKey = $"{_cacheKeyPrefix}_{id}";
_cache.Remove(cacheKey);
}
}
9.8 扩展方法
9.8.1 OQL扩展
using PWMIS.Core.Extensions;
// 直接返回列表
var users = OQL.FromObject<UserEntity>()
.Select()
.Where((cmp, u) => cmp.Property(u.Status) == 1)
.ToList(); // 扩展方法
// 直接返回单个对象
var user = OQL.FromObject<UserEntity>()
.Select()
.Where((cmp, u) => cmp.Property(u.ID) == 1)
.ToObject(); // 扩展方法
9.8.2 实体类扩展
public static class EntityExtensions
{
// 克隆实体
public static T Clone<T>(this T entity) where T : EntityBase, new()
{
var clone = new T();
foreach (var propName in entity.PropertyNames)
{
clone[propName] = entity[propName];
}
return clone;
}
// 复制属性
public static void CopyFrom<T>(this T target, T source) where T : EntityBase
{
foreach (var propName in source.PropertyNames)
{
target[propName] = source[propName];
}
}
// 判断是否为新记录
public static bool IsNew(this EntityBase entity)
{
if (string.IsNullOrEmpty(entity.IdentityName))
return true;
var idValue = entity[entity.IdentityName];
if (idValue == null)
return true;
if (idValue is int intValue)
return intValue == 0;
if (idValue is long longValue)
return longValue == 0;
return false;
}
}
9.9 本章小结
本章介绍了SOD框架的高级特性与扩展:
- 多数据库支持:配置和使用多种数据库
- 自定义数据提供程序:创建自己的数据库适配器
- 查询日志与调试:SQL日志记录和调试技巧
- 序列化:实体对象的序列化与反序列化
- 存储过程:调用数据库存储过程
- 动态SQL:构建动态SQL查询
- 性能优化:连接池、批量操作、缓存策略
- 扩展方法:OQL和实体类的扩展方法
掌握这些高级特性,可以让你更灵活地使用SOD框架,应对各种复杂的开发需求。
下一章预告:第十章将通过实战案例,展示SOD框架在真实项目中的应用和最佳实践。