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 ...
随机推荐
- Android之动画的学习(转载)
Android动画学习笔记-Android Animation 3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中 ...
- 转向Web
一直搞桌面软件开发,用C++绘界面那蛋疼的日子是记忆犹新,也不想钻研什么网络协议了,好好搞Web方面吧,它的开放很让人激动 Python是个好东西,适合许多场合,包括云计算,科学计算,软件测试,运维等 ...
- Waitforsingleobject 函数的用法
用户模式的线程同步机制效率高,如果需要考虑线程同步问题,应该首先考虑用户模式的线程同步方法. 但是,用户模式的线程同步有限制,对于多个进程之间的线程同步,用户模式的线程同步方法无能为力.这时,只能考虑 ...
- web测试常用的用例及知识
1. Web测试中关于登录的测试... 1 2. 搜索功能测试用例设计... 2 3. 翻页功能测试用例... 3 4. 输入框的测试... 5 5. ...
- mvc DropDownList默认选项
DDDContext db = new DDDContext(); List<SelectListItem> selectlistDistrict = new List<Select ...
- Java面试宝典答案详解与感悟(第二天)
19.构造器 Constructor 是否可被 override? 答案:构造器Constructor不能被继承,因此不能重写Override,但是可以被重载Overload. 解析:构造器:在面向对 ...
- C# 模拟键盘操作--SendKey(),SendKeys()
模拟键盘输入就是使用以下2个语法实现的.SendKeys.Send(string keys); //模拟汉字(文本)输入SendKeys.SendWait(string keys); //模拟按键输 ...
- Python—变量
1.在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量 2.访问限制: class内部属性可以被外部 ...
- http之100-continue(转)
1.http 100-continue用于客户端在发送POST数据给服务器前,征询服务器情况,看服务器是否处理POST的数据,如果不处理,客户端则不上传POST数据,如果处理,则POST上传数据.在现 ...
- R语言画图,根据正负值画不同颜色,并且画水平线或者垂直线
col=ifelse(x<0, "blue", "red") #如果x值为负值,用蓝色表示,反之,用红色表示 abline(v=0,col="g ...