09-高级特性与扩展

第九章:高级特性与扩展

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框架的高级特性与扩展:

  1. 多数据库支持:配置和使用多种数据库
  2. 自定义数据提供程序:创建自己的数据库适配器
  3. 查询日志与调试:SQL日志记录和调试技巧
  4. 序列化:实体对象的序列化与反序列化
  5. 存储过程:调用数据库存储过程
  6. 动态SQL:构建动态SQL查询
  7. 性能优化:连接池、批量操作、缓存策略
  8. 扩展方法:OQL和实体类的扩展方法

掌握这些高级特性,可以让你更灵活地使用SOD框架,应对各种复杂的开发需求。


下一章预告:第十章将通过实战案例,展示SOD框架在真实项目中的应用和最佳实践。

posted @ 2025-11-29 14:40  我才是银古  阅读(1)  评论(0)    收藏  举报