使用过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)的更多相关文章

  1. ServiceStack.OrmLite中的一些"陷阱"(2)

    注:此系列不是说ServiceStack.OrmLite的多个陷阱,这仅仅个人认为是某一个陷阱(毕竟我踩坑了)而引发的思考. 前文说到了项目需要使用两种不同的数据库语言,虽说前文问题已基本解决了,但是 ...

  2. ServiceStack.OrmLite中的一些"陷阱"(3)

    前文说到如果使用多数据库(不同SQL方言)时要如何开发?其实前文(第二篇)也有“透露”到.就是直接使用库提供的OrmLiteConnection 及OrmLiteConnectionFactory(I ...

  3. ServiceStack.OrmLite 调用存储过程

    最近在做关于ServiceStack.OrmLite调用存储过程时,有问题.发现ServiceStack.OrmLite不能调用存储过程,或者说不能实现我想要的需求.在做分页查询时,我需要传入参数传出 ...

  4. ServiceStack.OrmLite

    ServiceStack.OrmLite 谈谈我的入门级实体框架Loogn.OrmLite   每次看到有新的ORM的时候,我总会留意一下,因为自己也写过一个这样的框架,人总是有比较之心的.我可能会d ...

  5. ServiceStack.OrmLite T4模板使用记录

    前言 最近研究了下ServiceStack.OrmLite,文档中也提到了使用T4模板对数据库中已经有了表进行实体的映射,这里也顺便记录下使用的步骤和情况. 开始使用 引用T4模板 首先我们创建一个工 ...

  6. ServiceStack.OrmLite 入门(一)

    软件环境: Win7 x64 SP1 SQL Server 2008r2 Visual Studio 2017 Professional 目标:取出示例数据库 ReportServer 的表 Role ...

  7. JavaScript中的this陷阱的最全收集 没有之一

    当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概 念,可能真解释不清楚.有句话这么说:如果你不能向一个6岁小孩解 ...

  8. 转:JavaScript中的this陷阱的最全收集

    在其他地方看到的,觉得解释的狠详细,特此分享 当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概念,可能真解释不清 ...

  9. ServiceStack.OrmLite 学习笔记7-复杂点的使用1

    复杂点的使用1 先看看这2个类 class Customer { public int Id { get; set; } ... } class CustomerAddress { public in ...

随机推荐

  1. mysql使用小技巧

    1.mySql 删除表中大批量的数据 假设有一个表(logs)有2000万条记录,我们要在业 务不停止的情况下删除其中status=1的所有记录,差不多有1800万条,直接执行 DELETE FROM ...

  2. golang的channel使用

    chan使用前必须用make来创建,可以有多个槽位 0个槽位相当于消息,1个槽位相当于锁,多个槽位就是读者-写者 go func

  3. Cannot find or open the PDB file问题的解决

    'testcv.exe' (Win32): Loaded 'D:\Documents\Visual Studio 2013\Projects\testcv\x64\Debug\testcv.exe'. ...

  4. 查看已安装的CentOS版本信息:

    如何查看已安装的CentOS版本信息: 1)[root@localhost ~]# cat /proc/version Linux version 2.6.18-194.el5 (mockbuild@ ...

  5. Ubuntu:我不小心把/var/lock文件夹给删了

    在一个风和日丽的下午,不正常关闭minicom导致了device 没有正常解锁,于是使用minicom的时候提示 device is locked: 根据网上看到的方法只要把/var/lock 里面的 ...

  6. android 各种xml资源的引用方式

    更多资源类型 本页定义了其它一些具体的资源类型,包括: Bool 存放布尔值的XML资源. Color 存放颜色值的XML资源(十六进制颜色). Dimension 存放数量值的XML资源(带计量单位 ...

  7. ajax将json写到table中去

    查询条件: <table style="width: 100%;border-collapse: collapse;" > <tr> <th styl ...

  8. PTA Strongly Connected Components

    Write a program to find the strongly connected components in a digraph. Format of functions: void St ...

  9. Android之简单了解Bitmap显示图片及缓存图片

    昨天我们学了如何连接网络,今天我们就学习一下如何从把网上图片显示到项目中 今天主要用到的是Bitmap 类 Bitmap是Android系统中的图像处理的最重要类之一.用它可以获取图像文件信息,进行图 ...

  10. font-family 字体

    宋体 SimSun黑体 SimHei微软雅黑 Microsoft YaHei微软正黑体 Microsoft JhengHei新宋体 NSimSun新细明体 PMingLiU细明体 MingLiU标楷体 ...