ServiceStack.OrmLite中的一些"陷阱"(1)
使用过ServiceStack.Ormlite的人都应该知道,其作为一个轻量级的ORM,使用的便捷度非常高,用起来就一个字:爽!
而支撑其便捷度的,是库内大量地使用了扩展方法及静态变量。
首先先从源头入手分析(以下以Sqlite为例):
OrmLiteConfig.DialectProvider = SqliteOrmLiteDialectProvider.Instance;
using (IDbConnection db = "~/db.sqlite".MapAbsolutePath().OpenDbConnection())
{
db.CreateTable<User>();
db.Insert(new User("Hello", "Password"));
}
其中MapAbsolutePath()是转换为绝对路径,这里不详细分析。而OPenDbConnection()则是OrmLiteConfig.cs上的扩展方法,其调用链如下:
public static IDbConnection OpenDbConnection(this string dbConnectionStringOrFilePath)
{
var sqlConn = dbConnectionStringOrFilePath.ToDbConnection(DialectProvider);
sqlConn.Open();
return sqlConn;
}
public static IDbConnection ToDbConnection(this string dbConnectionStringOrFilePath)
{
return dbConnectionStringOrFilePath.ToDbConnection(DialectProvider);
} public static IDbConnection ToDbConnection(this string dbConnectionStringOrFilePath, IOrmLiteDialectProvider dialectProvider)
{
var dbConn = dialectProvider.CreateConnection(dbConnectionStringOrFilePath, options: null);
return dbConn;
}
从重载方法中可以看出如果不提供IOrmLiteDialectProvider 参数的话是会使用OrmLiteConfig.DialectProvider (type:IOrmLiteDialectProvider)这个静态属性作为默认值的。而IOrmLiteDialectProvider 的作用就是ORM基于不同数据库语言的实现,ORM生成各自的SQL语句都靠它。
那么,既然有 ToDbConnection(string,IOrmLiteDialectProvider) 这样的函数,难道就可以在同一个项目中混合使用多个不同数据库语言的IDbConnect 了吗?(有可能项目有这样的要求)
例如这样:
var sqliteDb = "sqlitexxxx".ToDbConnection(SqliteOrmLiteDialectProvider.Instance);
var mysqlDb = "mysqlxxxx".ToDbConnection(MySqlDialectProvider.Instance);
我只能说:太!天!真!了!在OrmLite中大量地使用了OrmLiteConfig.DialectProvider 静态属性,以WriteConnectionExtensions.Delete<T>为例:
public static int Delete<T>(this IDbConnection dbConn, Expression<Func<T, bool>> where)
{
return dbConn.Exec(dbCmd => dbCmd.Delete(where));
} public static int Delete<T>(this IDbCommand dbCmd, Expression<Func<T, bool>> where)
{
var ev = OrmLiteConfig.DialectProvider.SqlExpression<T>();
ev.Where(where);
return dbCmd.Delete(ev);
} public static T Exec<T>(this IDbConnection dbConn, Func<IDbCommand, T> filter)
{
var holdProvider = OrmLiteConfig.TSDialectProvider;
try
{
var ormLiteDbConn = dbConn as OrmLiteConnection;
if (ormLiteDbConn != null)
OrmLiteConfig.TSDialectProvider = ormLiteDbConn.Factory.DialectProvider; using (var dbCmd = dbConn.CreateCommand())
{
dbCmd.Transaction = (ormLiteDbConn != null) ? ormLiteDbConn.Transaction : OrmLiteConfig.TSTransaction;
dbCmd.CommandTimeout = OrmLiteConfig.CommandTimeout;
var ret = filter(dbCmd);
LastCommandText = dbCmd.CommandText;
return ret;
}
}
finally
{
OrmLiteConfig.TSDialectProvider = holdProvider;
}
}
上述代码中可以看出,IDbCommand的Delete扩展方法中就使用了OrmLiteConfig.DialectProvider 静态属性,所以我们使用前必须赋予初始值。
有细心的网友可能会发现,在Exec方法中用到的Provider是TSDialectProvider 而非 DialectProvider,过中原由得慢慢分析。
[ThreadStatic]
public static IOrmLiteDialectProvider TSDialectProvider; private static IOrmLiteDialectProvider dialectProvider;
public static IOrmLiteDialectProvider DialectProvider
{
get
{
if (dialectProvider == null)
{
throw new ArgumentNullException("DialectProvider",
"You must set the singleton 'OrmLiteConfig.DialectProvider' to use the OrmLiteWriteExtensions");
}
return TSDialectProvider ?? dialectProvider;
}
set
{
dialectProvider = value;
}
}
先看TSDialectProvider,这是线程安全的静态变量。反而比较有意思的是DialectProvider,首先在get的时候dialectProvider 字段不能为null,否则会抛出异常。但是返回时却优先返回TSDialectProvider,而不是dialectProvider。为什么呢?
我觉得这是出于便捷性和多线程方面的考虑。
首先是便捷性,如果不便捷而较为常规的做法是怎样,我的实现是直接提供TSDialectProvider。
[ThreadStatic]
private static IOrmLiteDialectProvider tsDialectProvider; public static IOrmLiteDialectProvider DialectProvider
{
get
{
return tsDialectProvider;
}
set
{
tsDialectProvider = value;
}
}
多线程使用时必先在打开连接前先初始化DialectProvider。
public class OrmLiteTest
{
public static void Main(string[] args)
{
new Thread(SqliteTest).Start("sqlite");
new Thread(MySqlTest).Start("mysql");
} static void SqliteTest(string path)
{
OrmLiteConfig.DialectProvider = SqliteOrmLiteDialectProvider.Instance;
var db = ((string)path).OpenDbConnection();
//TODO sth.
}
static void MySqlTest(string path)
{
OrmLiteConfig.DialectProvider = MySqlDialectProvider.Instance;
var db = ((string)path).OpenDbConnection();
//TODO sth.
}
}
这里顺道把多线程也解决了,通常只使用一种数据库语言的时候,需要并发执行数据库操作(多个线程,多个IDbConnection)时,每次都需提前设置OrmLiteConfig.DialectProvider 反而会显得烦琐。
return TSDialectProvider ?? dialectProvider;
因为一般情况下TSDialectProvider默认为null,除非需要用到多种数据库才需要手动设置。可以说,TSDialectProvider就是专门为多数据库语言而设的。
ServiceStack.OrmLite中的一些"陷阱"(1)的更多相关文章
- ServiceStack.OrmLite中的一些"陷阱"(2)
注:此系列不是说ServiceStack.OrmLite的多个陷阱,这仅仅个人认为是某一个陷阱(毕竟我踩坑了)而引发的思考. 前文说到了项目需要使用两种不同的数据库语言,虽说前文问题已基本解决了,但是 ...
- ServiceStack.OrmLite中的一些"陷阱"(3)
前文说到如果使用多数据库(不同SQL方言)时要如何开发?其实前文(第二篇)也有“透露”到.就是直接使用库提供的OrmLiteConnection 及OrmLiteConnectionFactory(I ...
- ServiceStack.OrmLite 调用存储过程
最近在做关于ServiceStack.OrmLite调用存储过程时,有问题.发现ServiceStack.OrmLite不能调用存储过程,或者说不能实现我想要的需求.在做分页查询时,我需要传入参数传出 ...
- ServiceStack.OrmLite
ServiceStack.OrmLite 谈谈我的入门级实体框架Loogn.OrmLite 每次看到有新的ORM的时候,我总会留意一下,因为自己也写过一个这样的框架,人总是有比较之心的.我可能会d ...
- ServiceStack.OrmLite T4模板使用记录
前言 最近研究了下ServiceStack.OrmLite,文档中也提到了使用T4模板对数据库中已经有了表进行实体的映射,这里也顺便记录下使用的步骤和情况. 开始使用 引用T4模板 首先我们创建一个工 ...
- ServiceStack.OrmLite 入门(一)
软件环境: Win7 x64 SP1 SQL Server 2008r2 Visual Studio 2017 Professional 目标:取出示例数据库 ReportServer 的表 Role ...
- JavaScript中的this陷阱的最全收集 没有之一
当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概 念,可能真解释不清楚.有句话这么说:如果你不能向一个6岁小孩解 ...
- 转:JavaScript中的this陷阱的最全收集
在其他地方看到的,觉得解释的狠详细,特此分享 当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概念,可能真解释不清 ...
- ServiceStack.OrmLite 学习笔记7-复杂点的使用1
复杂点的使用1 先看看这2个类 class Customer { public int Id { get; set; } ... } class CustomerAddress { public in ...
随机推荐
- Java编写最大公约数和最小公倍数
package javaapplication24; class NegativeIntegerException extends Exception{ String message; public ...
- sqlserver 查看正在执行sql
SELECT [session_id], [request_id], [cpu_time], [start_time] AS '开始时间', [status] AS '状态', [co ...
- DragSelectRecyclerView 长按滑动多选图像android特效
高仿Google相册多选效果,长按某一item后然后滑动选择到任意item,效果很不错,适合相册页面多选部分效果. 本例子主要是自定义DragSelectRecyclerView通过如下展示gridv ...
- Unity3D 物体移动方式
1. 简介 在Unity3D中,有多种方式可以改变物体的坐标,实现移动的目的,其本质是每帧修改物体的position. 2. 通过Transform组件移动物体 Transform 组件用于描述物体在 ...
- python之路——基础篇(2)模块
模块:os.sys.time.logging.json/pickle.hashlib.random.re 模块分为三种: 自定义模块 第三方模块 内置模块 自定义模块 1.定义模块 将一系列功能函数或 ...
- CC1310之使用SMARTRF STUDIO
SMARTRF STUDIO是TI提供的射频测试软件,在调射频的时候非常非常非常好用,推荐每一个使用TI射频芯片的工程师都要掌握. 1 如何使用? 要使用SMARTRF STUDIO,硬件必须连接仿真 ...
- privoxy代理google浏览器访问缓慢
取消--no-daemon <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PU ...
- db2 常用函数
语法:VALUE(EXPRESSION1,EXPRESSION2) VALUE函数是用返回一个非空的值,当其第一个参数非空,直接返回该参数的值,如果第一个参数为空,则返回第一个参数的值. eg: -- ...
- 转:fatal error: SDL/SDL.h: No such file or directory
Ubuntu的新得立已经包含SDL库,所以通过几个简单的命令就可以安装,比windows还傻瓜! sudo apt-get install libsdl1.2-dev(比较大,10M左右) 附加包: ...
- Redis的5种数据结构
Redis可以存储可以存储键与5种不同数据结构类型之间的映射. 五种结构类型为:STRING(字符串).LIST(列表).SET(集合).HASH(散列).ZSET(有序集合). 1.字符串类型Str ...