SuperHelper——灵活通用的、开源的.Net ORM微型框架

SuperHelper是博主利用业余时间编写的一个ORM微型框架,除了可以提高开发效率,与其它ORM框架相比,博主更加喜欢SuperHelper的使用简单、适用范围广的特点。

 

简介

  SuperHelper是一个基于.Net平台的非侵入式的的微型ORM框架,可帮助开发者快速简便的访问数据库并对其操作,且部署起来十分简单;只依赖于相应的数据库引擎,开发者可以根据实际项目需要增加引用不同版本的SuperHelper组件,且不会产生冲突。(目前SuperHelper有SQlServer版和SQLite版)

以下是SuperHelper的概要特点:

1、部署十分简单。开发者只需在项目配置文件中为SuperHelper指定一个可用的连接字符串即可完成部署。

2、适用范围广。SuperHelper可以在经典三层架构项目、一般处理程序+模板引擎项目、WebForm、MVC架构项目等等都可以使用,项目中如果需要切换使用不同数据库引擎则只需切换相应版本的SuperHelper组件即可。还有,SuperHelper可以与其它ORM框架如微软的EF、NHibernate等混可使用且不会产生任何冲突。同时因为SuperHelper是一个非侵入式的ORM框架,项目不会对SuperHelper有过多的依赖,开发者依然可以把代码很方便的迁移到其他地方。

3、使用灵活。写好sql语句之后,开发者只需要再写一行代码即可完成访问数据库并返回相应数据实体的操作(SuperHelper还支持对实体类的复杂类型属性或字段赋值)。ps:与一些微型ORM一样,SuperHelper是不支持LinQ的,不过针对SQlServer,SuperHelper支持存储过程的调用。

使用方法

下面的使用方法介绍中,如无特别说明,皆以SuperHelper的SQlServer版本为例。

1、部署:

  首先在项目的配置文件中为SuperHelper指定一条可用的数据库连接字符串,将其Name属性设置为“SuperHelper_SQLServer”,如下代码所示。(这里是根据不同版本的SuperHelper组件的命名空间决定,如果使用SuperHelper的SQLite版,则Name属性应为“SuperHelper_SQLite”)。

<connectionStrings>
    <add name="SuperHelper_SQLServer" connectionString="Data Source=127.0.0.1;Initial Catalog=News;Integrated Security=True"/>
  </connectionStrings>

  至此,SuperHelper的部署工作就已经完成了,接下来,只需要在解决方案的某个项目中添加引用SuperHelper_SQLServer组件即可。

2、SuperHelper的方法调用

  对于SuperHelper而言,所有数据库访问操作分为两种:查询、非查询(增,删、改)。

  为了演示方便,博主准备一个测试用的新闻数据库News和相应的实体类,如果大家习惯使用自己数据库来测试,则可以跳过准备过程,直接看代码。

  下面是News数据库的sql语句脚本,用于生成该数据库的架构和少量的测试数据。

 1 CREATE DATABASE [News]
 2  CONTAINMENT = NONE
 3  ON  PRIMARY 
 4 ( NAME = NNews, FILENAME = N此处要改为本地路径\News.mdf , SIZE = 5120KB , MAXSIZE = 10240KB , FILEGROWTH = 1024KB )
 5  LOG ON 
 6 ( NAME = NNews_log, FILENAME = N此处要改为本地路径\News.ldf , SIZE = 2048KB , MAXSIZE = 10240KB , FILEGROWTH = 3072KB )
 7 
 8 USE [News]
 9 CREATE TABLE [dbo].[NewsInfo](
10     [NewsID] [int] IDENTITY(1,1) NOT NULL,
11     [NewsTitle] [nvarchar](50) NULL,
12     [SubTime] [datetime] NULL,
13     [TypeID] [int] NULL,
14     [NewsContent] [nvarchar](max) NULL
15     )
16 
17 
18 CREATE TABLE [dbo].[TypeInfo](
19     [TypeID] [int] IDENTITY(1,1) NOT NULL,
20     [TypeTitle] [nvarchar](50) NULL
21     )
22 
23 USE [News]
24 GO
25 SET IDENTITY_INSERT [dbo].[NewsInfo] ON 
26 
27 GO
28 INSERT [dbo].[NewsInfo] ([NewsID], [NewsTitle], [SubTime], [TypeID], [NewsContent]) VALUES (1, N测试, CAST(N2014-10-30 00:00:00.000 AS DateTime), 1, N测试文档)
29 GO
30 INSERT [dbo].[NewsInfo] ([NewsID], [NewsTitle], [SubTime], [TypeID], [NewsContent]) VALUES (2, N测试123, CAST(N2013-05-06 00:00:00.000 AS DateTime), 2, N这是一张大图)
31 GO
32 INSERT [dbo].[NewsInfo] ([NewsID], [NewsTitle], [SubTime], [TypeID], [NewsContent]) VALUES (3, Ntest456, CAST(N2013-06-07 00:00:00.000 AS DateTime), 3, N545616516)
33 GO
34 INSERT [dbo].[NewsInfo] ([NewsID], [NewsTitle], [SubTime], [TypeID], [NewsContent]) VALUES (5, N你好, CAST(N2014-05-06 00:00:00.000 AS DateTime), 2, Nsdfvszgvsz)
35 GO
36 INSERT [dbo].[NewsInfo] ([NewsID], [NewsTitle], [SubTime], [TypeID], [NewsContent]) VALUES (6, NAPEC, CAST(N2013-08-06 00:00:00.000 AS DateTime), 1, N354asefsef65)
37 GO
38 INSERT [dbo].[NewsInfo] ([NewsID], [NewsTitle], [SubTime], [TypeID], [NewsContent]) VALUES (7, Nhahahah, CAST(N2014-06-08 00:00:00.000 AS DateTime), 2, Nsrgdfs)
39 GO
40 INSERT [dbo].[NewsInfo] ([NewsID], [NewsTitle], [SubTime], [TypeID], [NewsContent]) VALUES (8, Nweqwe, CAST(N2012-01-05 00:00:00.000 AS DateTime), 4, N5465dfsdfv)
41 GO
42 SET IDENTITY_INSERT [dbo].[NewsInfo] OFF
43 GO
44 SET IDENTITY_INSERT [dbo].[TypeInfo] ON 
45 
46 GO
47 INSERT [dbo].[TypeInfo] ([TypeID], [TypeTitle]) VALUES (1, N文字)
48 GO
49 INSERT [dbo].[TypeInfo] ([TypeID], [TypeTitle]) VALUES (2, N图片)
50 GO
51 INSERT [dbo].[TypeInfo] ([TypeID], [TypeTitle]) VALUES (3, N视频)
52 GO
53 INSERT [dbo].[TypeInfo] ([TypeID], [TypeTitle]) VALUES (4, N音频)
54 GO
55 SET IDENTITY_INSERT [dbo].[TypeInfo] OFF
56 GO
News数据库生成脚本

  数据库准备完成后,接下来是准备相应的数据库实体类。

 1     public class NewsInfo
 2     {
 3         public int NewsID { get; set; }
 4         public string NewsTitle { get; set; }
 5         public DateTime SubTime { get; set; }
 6         public int TypeID { get; set; }
 7         public string NewsContent { get; set; }
 8     }
 9 
10     public class TypeInfo
11     {
12         public int TypeID { get; set; }
13         public string TypeTitle { get; set; }
14     }
实体类定义

  

至此,准备过程已完成,下面我们先来执行一条简单的sql语句。

 1 using SuperHelper_SQLServer;
 2 
 3 namespace SuperHelper演示demo
 4 {
 5     public partial class Form1 : Form
 6     {
 7         public Form1()
 8         {
 9             InitializeComponent();
10         }
11 
12         private void Form1_Load(object sender, EventArgs e)
13         {
14                 string sql = "select * from NewsInfo";
15 
16                 List<NewsInfo> lst_news = SH_Query<NewsInfo>.GetListBySql(sql);
17 
18                 dgv.DataSource = lst_news;
19             }
20         }
21     }

 

接下来,我们再来执行有参数的sql语句。(重复的代码,博主就不再贴出来,只贴关键的sql语句和执行代码)

 1 //查询NewsID=1 和 TypeID=2 的新闻信息
 2 
 3 #region 这是第一种写法
 4 string sql = "select * from NewsInfo where NewsID = @NewsID and TypeID = @TypeID";
 5 NewsInfo newinfo = new NewsInfo();
 6 newinfo.NewsID = 1;
  newinfo.TypeID = 2;
7 8 List<NewsInfo> lst_news = SH_Query<NewsInfo>.GetListBySql(sql, new List<object> { newinfo}); 9 #endregion 10 11 #region 这是第二种写法 12 string sql = "select * from NewsInfo where NewsID = @a and TypeID = @b"; 13 int i = 1;
  int j = 2;
14 15 List<NewsInfo> lst_news = SH_Query<NewsInfo>.GetListBySql(sql, new List<object> { i, j }); 16 #endregion

  看到这里,估计大家也能对SuperHelper的用法和原理有大概的认识了。

  SuperHelper其实是根据匹配实体类的公共属性或者公共字段名称与执行sql语句的查询结果集中的列名称来产生映射关系;所以,如果实体类的属性或者字段名与sql语句查询的结果集的列名不一致(名称大小写也必须一致),那么就无法正确获取数据了。根据上面的代码举个例子:如果NewsInfo类中的NewsTitle属性改为newsinfo,而News数据库中NewsInfo表的NewsTitle列名保持不变,而因为上述sql语句执行后的结果集列名中只有“NewsTitle”而没有“newsinfo”,所以最终结果就是lst_news中的对象的NewsTitle属性都为空。

  至于sql语句的参数匹配方式,大家可以参考下面的两种方法。

  第一种写法:根据实体类对象的属性或者字段名与sql语句中的参数名进行匹配。同样的,根据上面的第一种写法代码举个例子:sql语句中的@NewsID必须与实体类对象newsinfo的属性名NewsID大小写一致,这样才能1赋值给参数@NewsID。

  第二种写法:根据参数顺序匹配。同样的,根据上面的第二种写法代码举个例子:i 的值会赋给sql语句中第一个出现的参数,而不管参数@a改成什么名字。如果sql语句中同一个参数出现多次,则以该参数第一次出现的位置为准,例如sql语句是这样“..@b.....@a.....@b...@c....”,则参数集合new List<object>{i, j, k}中 i 会赋值给@b,j 赋值给@a,k 赋值给@c。

  其实,上述两种匹配方法是可以混合使用的,由此可以得出第三种写法,代码如下:

1 //查询NewsID=1 和 TypeID = 2 的新闻信息
2 
3 string sql = "select * from NewsInfo where NewsID = @NewsID and TypeID = @a";
4 
5 NewsInfo newinfo = new NewsInfo();
6 newinfo.NewsID = 1;
7 int i = 2;
8 
9 List<NewsInfo> lst_news = SH_Query<NewsInfo>.GetListBySql(sql, new List<object> { newinfo, i});

  SuperHelper会优先根据实体类的属性与字段与sql语句中的参数进行匹配,而剩下的无法与实体类匹配成功的参数则按照顺序来进行二次匹配(ps:已经与实体类匹配好的sql参数不会参加二次匹配的排序)

 

  上面简介中还提到,SuperHelper还支持对实体类的复杂类型属性或字段赋值,为了演示这个功能,我们需要对上述NewsInfo类中添加一个class类型的属性,完整NewsInfo定义如下:

 1     public class NewsInfo
 2     {
 3         public int NewsID { get; set; }
 4         public string NewsTitle { get; set; }
 5         public DateTime SubTime { get; set; }
 6         public int TypeID { get; set; }
 7         public string NewsContent { get; set; }
 8 
 9         public TypeInfo Ti { get; set; }  //TypeInfo类是上面准备过程中定义好的类
10     }

  对于这个TypeInfo类型的属性Ti,即使数据库中NewsInfo表中也含有名称为Ti的列,使用上述提到的sql语句是没办法完成数据获取的,需要对其sql语句进行稍加修改,代码如下:

1 //对实体类中的复杂属性或字段进行数据匹配
2 
3 string sql = "select NewsID,NewsTitle,NewsContent,TypeTitle as [Ti.TypeTitle] from NewsInfo left join TypeInfo on NewsInfo.TypeID=TypeInfo.TypeID";
4 
5 List<NewsInfo> lst_news = SH_Query<NewsInfo>.GetListBySql(sql);

  此时,大家可以断点调试,查看lst_news中NewsInfo对象中Ti属性中的TypeTitle属性赋值已经成功了。其实整个修改过程也很简单,就是将查询结果集中的列名“TypeTitle”改成“Ti.TypeTitle”即可。这就跟你在VS中写代码一样,想要获取某个类的属性时,只需用“.”就可以获取到了,意思是一样的。当然,如果Ti中也含有class类型的属性或字段时,可以继续以“.”方式对其进行赋值,如:“Ti.class类型的属性名或字段名.class类型的属性名或字段名.class类型的属性名或字段名”,以此类推。

  至此,SuperHelper中主要的查询方法的使用要点已经基本介绍完毕了,至于非查询的方法使用与查询方法基本一致,只不过非查询固定返回值是受影响行数而已。当然,SuperHelper中还有支持临时切换访问不同数据库,SQLServer版还支持存储过程等等。但博文篇幅有限,在此就不多详述了,有时间的话博主会再详细写其它用法。

  SuperHelper源码地址:https://github.com/honker-bin/SuperHelper

 

  最后再说几句话,博主不是什么技术大牛,但是大牛之所以是大牛,除了技术牛X之外,善于分享是他们共同的特点。其实很多程序员朋友在长期的开发过程积累了不少有用的技术与经验,不是不想分享,而是懒得分享,但是要知道,技术除了不断学习,分享和交流也是迅速提高自身的捷径之一(这一点博主深有体会)。最后调侃一下,这年头连微软都开源了,你还懒得分享自己的技术经验吗?:)

 

作者:冯礼斌

文章出处:http://www.cnblogs.com/fenglibin/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。