从零开始编写自己的C#框架(6)——SubSonic3.0插件介绍(附源码)

原文:从零开始编写自己的C#框架(6)——SubSonic3.0插件介绍(附源码)

  前面几章主要是概念性的东西为主,向初学者们介绍项目开始前的一些知识与内容,从本章开始将会进入实操阶段,希望跟着本系统学习的朋友认真按说明做好每一步操作(对于代码最好是直接照着文档内容在你的IDE中打一次出来,而不是使用复制粘贴),这样对你理解后面的章节会有较好的帮助,如果你对我这种书写方式有什么建议或支持,也希望在评论中留言,谢谢你的支持。

  SubSonic3.0简介

  SubSonicRob Conery用c#语言写的一 个ORM开源框架,使用BSD软件授权许可(The BSD
3-Clause
License)。它是一个实用的快速开发框架,通过非常简单的配置,以及附带的T4模板,就可以帮我们生成功能强大的数据访问层工具,让开发人员远离SQL语句的拼接,专注于业务逻辑的开发。

  SubSonic3.0适合短、平、快的中小型项目开发,支持MsSql、MySql与SQLite三种数据库,支持Linq和支持事务。对数据库操作灵活,对生成的SQL语句会自动进行优化,虽然是ORM框架,但在性能上并没有太大的损失。它上手容易,是一个非常棒的ORM开发框架。

  3.0版本最大的缺点是框架未开发完善,对多表关联查询支持的不是很好,只支持一个多表关联条件(复杂的多表关联只能使用存储过程或SQL语句拼接方式来实现);条件语句对In与Not
In不支持(需要在数据层重新封装处理才行);如果使用Redis缓存的话,存储数据时对subsonic3.0生成的实体有兼容问题,需要做一次转换封装后才能使用。

  不过总的来说,这些缺点都是可以克服的,在开发效率、二次开发、软件维护上来说,优点也是非常明显的,请大家耐心学完本系列,你就能很容易的掌握SubSonic3.0这个开发利器。

  SubSonic3.0安装说明

  SubSonic3.0安装练习相关附件下载

  在MsSql中新建一个数据库SubSonicTest,并设置好登陆帐号与密码为SubSonicTest。

  运行下载包里的“SQL语句.txt”文件里的语句,生成数据表、记录与测试用的存储过程。

  

  创建一个空白解决方案

  

  在解决方案中创建一个空Web应用程序项目

  

  将SubSonic文件夹复制进项目文件夹中

  

  刷新项目就可以看到隐藏的SubSonic文件夹

  

  打开Web.config,添加数据库链接字串

  

  

<connectionStrings>
<!-- 连接数据库的字符串 -->
<add name="procom" connectionString="server=.;Initial Catalog=SubSonicTest;uid=SubSonicTest;pwd=SubSonicTest"/>
</connectionStrings>

View
Code

  将Dll和SubSonic.Core复制到解决方案中

  

  添加SubSonic3.0项目(直接使用源码项目,方便在使用时调试及修改)

  

  

  

  添加SubSonic.Core项目和Castle.Core.dll引用(大神TerryLee的博客里有Castle
开发系列文章
有兴趣的朋友可以进去学习一下)

  

  

  

  将SubSonic包含进项目中

  

  如果你的数据库名称、帐号和密码设置同面前要求一样的话,T4模板将会直接运行生成相关实体类,如果设置不一样的,请按下面要求来进行设置

  打开Settings.ttinclude配置文件

  

  

  设置好以后,选择全部T4模板文件,运行生成代码

  

  

  SubSonic3.0模板介绍说明

  本文只介绍附件中SubSonic文件夹模板(MsSql),不适用官方发布的模板



































文件名称 说明
ActiveRecord.tt 按数据表生成对应名称的类实体(Model),以及支持lambda表达式的各种常用函数(包括增、改、查、删、Exists等)
Context.tt 生成常用公共模版函数:Select、Insert、Avg、Count、Max、Min、Variance、StandardDeviation、Sum、Delete、GetQuery、Update等
CreateModel.tt  新增模板(原SubSonic3.0没有这个模板),主要功能是数据表生成对应名称的类实体(Model),这是普通的类实体,不附任何多余的内容(函数),主要用于存储到Redis缓存或做C/S接口通讯时转Json使用。非必须项
EntityTable.tt 新增模板(原SubSonic3.0没有这个模板,里面的功能从Structs.tt模板中分离出来的),主要功能是可直接获取数据表名称与字段名称,减少开发中硬编码的存在
Settings.ttinclude 模版参数配置
SQLServer.ttinclude MsSql数据库读取、生成的相关配置
StoredProcedures.tt 生成存储过程调用函数(本模板生成的函数调用存储过程非常方便,详情请关注例子)
Structs.tt 生成表结构模版,提供给SubSonic插件调用

  SubSonic3.0使用例子

  点击下载本文本例子的解决方案源码

  之前发布过《SubSonic3.0使用例子》,当时刚开始接触写得有点乱,现在重新整理一下,使用上面生成的类来进行操作,可以在项目中直接运行。对于下面的例子,可以直接在解决方案中Debug运行,查看运行结果,以加深理解,当然大部分功能在正式开发中很少使用,所以简单了解一下就可以了,只要会用Lambda,那么后面相关章节介绍时你就会使用。

  下面例子只适用于本文所附带的模板,特此说明

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using Solution.DataAccess.DataModel;
using SubSonic.Linq.Structure;
using SubSonic.Query;

namespace SubSonicTest
{
public partial class Test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//-------------------------------------------------------------------------------
// 新增记录
//-------------------------------------------------------------------------------
var branch = new Branch();
branch.DeptCode = SPs.P_Branch_GetMaxBranchCode(2, 1).ExecuteScalar() + "";
branch.Name = "客服部";
branch.Comment = "客户售后服务维护部门";
branch.ParentId = 1;
branch.Sort = 7;
branch.Depth = 2;
//AddDate这个属性不用赋值,ORM框架生成更新语句时会自动过滤该字段,更新时将会使用数据库中设置的默认值
//branch.AddDate = DateTime.Now;

//保存进数据库
branch.Save();

WriteLine("记录添加成功,新增Id为:" + branch.Id);

//-------------------------------------------------------------------------------
// 修改记录
//-------------------------------------------------------------------------------
//从数据库中读出刚添加的记录
var branchModel = Branch.SingleOrDefault(x => x.Id == branch.Id);
//也可以用这个
//var branchModel = new Branch(x => x.Id == branch.Id);
//打印读取出来的数据
WriteLine(branchModel.ToString());

//修改名称为“售后服务部”
branchModel.Name = "售后服务部";
//保存进数据库
branchModel.Save();

//重新从数据库中读取出来并打印到输出窗口
WriteLine((new Branch(x => x.Id == branch.Id)).ToString());

//-------------------------------------------------------------------------------
// 判断刚刚修改的记录是否存在
//-------------------------------------------------------------------------------
if (Branch.Exists(x => x.Id == branch.Id))
{
WriteLine("Id为" + branch.Id + "的记录存在!");
}
else
{
WriteLine("Id为" + branch.Id + "的记录不存在!");
}

//-------------------------------------------------------------------------------
// 删除记录
//-------------------------------------------------------------------------------
//删除当前记录
Branch.Delete(x => x.Id == branch.Id);
//也可以使用这种方法删除
//branchModel.Delete();
WriteLine("删除Id为" + branch.Id + "的记录成功!");
//除了上面两种删除方法外,还有好几种其它方法,不过都不常用,这里就不详细罗列出来,大家如果有兴趣可以查看我之前发布的博客

//-------------------------------------------------------------------------------
// 判断刚刚删除的记录是否存在
//-------------------------------------------------------------------------------
if (Branch.Exists(x => x.Id == branch.Id))
{
WriteLine("Id为" + branch.Id + "的记录存在!");
}
else
{
WriteLine("Id为" + branch.Id + "的记录不存在!");
}

//-------------------------------------------------------------------------------
// 使用类实体附带的函数查询
//-------------------------------------------------------------------------------
//查找出所有记录——使用All()
var list = Branch.All();
foreach (var tem in list)
{
//打印到输出窗口
WriteLine(tem.ToString());
}

//查询指定条件的记录——使用Find()
IList<Branch> il = Branch.Find(x => x.Id > 0);
foreach (var tem in il)
{
//打印到输出窗口
WriteLine(tem.ToString());
}

//获取第2页记录(每页3条记录)
il = Branch.GetPaged("Id Desc", 2, 3);
foreach (var tem in il)
{
//打印到输出窗口
WriteLine(tem.ToString());
}

//使用Id倒序排序,获取第3页记录(每页3条记录)
il = Branch.GetPaged("Id Desc", 3, 3);
foreach (var tem in il)
{
//打印到输出窗口
WriteLine(tem.ToString());
}

//-------------------------------------------------------------------------------
// 使用Select类查询
//-------------------------------------------------------------------------------
//创建Select对象
//var select = new Select();
//显示指定的列
var select = new Select(new string[] { BranchTable.Id, BranchTable.Name, BranchTable.DeptCode });
//指定查询表
select.From<Branch>();

//运行完上面这条语句后,SQL已经生成出来了,大家在Debug状态将鼠标指向select就可以看到,往下继续执行时,每添加一个属性都会添加在生成的SQL语句中

//添加查询条件——Id大于2且编号头两位为01的部门
select.Where(BranchTable.Id).IsGreaterThanOrEqualTo(2).And(BranchTable.DeptCode).StartsWith("01");
//查询时括号添加例子
//select.Openexpression_r().Where("").IsEqualTo(0).Or("").IsEqualTo(11).Closeexpression_r().And("").IsEqualTo(3);

//设置去重复——SubSonic没有去重复选项,需要自己手动修改Dll源码
select.Distinct(true);
//或
//select.IsDistinct = true;

//设置查询数量
select.Top("5");

//添加排序——倒序
select.OrderDesc(BranchTable.Sort);
//或
//List<string> orderbyList = new List<string>();
//orderbyList.Add(BranchTable.Sort + " Desc");
//orderbyList.Add(BranchTable.Id + " Desc");
//select.OrderBys = orderbyList;

//设置分页,获取第一页记录(每页10条记录)
select.Paged(1, 10);

//执行查询——返回DataTable
var dt = select.ExecuteDataTable();
if (dt != null && dt.Rows.Count > 0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
//打印到输出窗口
WriteLine(dt.Rows[i][BranchTable.Id] + " " + dt.Rows[i][BranchTable.Name]);
}
}

//执行查询——返回List
var bl = select.ToList<Branch>();
foreach (var tem in bl)
{
//打印到输出窗口
WriteLine(tem.ToString());
}

//查询总记录数
var recordCount = select.GetRecordCount();
//打印到输出窗口
WriteLine("记录数量:" + recordCount.ToString());

//select提供了很多接口,可以返回很多不同的数据,具体大家可以根据需求来获取
//Select与SqlQuery 主要用于条件等属性不确定时拼接查询,如果大家需要使用相关内容,可以留意后面的章节源码,我已将相关的功能封装到一些类中了,大家可以直接调用

//-------------------------------------------------------------------------------
// HotelDBDB查询类的使用方式
//-------------------------------------------------------------------------------
//使用数据库名称+DB作为名称的类,可以直接调用聚合函数
var db = new SubSonicTestDB();
//平均值
WriteLine("平均值:" + db.Avg<Branch>(x => x.Id).Where<Branch>(x => x.Id < 10).ExecuteScalar() + "");
//计算数量
WriteLine("计算数量:" + db.Count<Branch>(x => x.Id).ExecuteScalar() + "");
//计算合计数量
WriteLine("计算合计数量:" + db.Sum<Branch>(x => x.Id).ExecuteScalar() + "");

//最大值
WriteLine("最大值:" + db.Max<Branch>(x => x.Id).ExecuteScalar() + "");
//最小值
WriteLine("最小值:" + db.Min<Branch>(x => x.Id).ExecuteScalar() + "");

//-------------------------------------------------------------------------------
// 存储过程调用方法
//-------------------------------------------------------------------------------
//使用SPs.存储过程名称(参数1, 参数2, 参数3);就可以调用存储过程
var obj = SPs.P_Branch_GetMaxBranchCode(1, 0).ExecuteScalar();
WriteLine(obj + "");
//可根据存储过程返回的数据,调用不同的Execute来获取
//大家试试在SPs.P_Branch_GetMaxBranchCode(1, 2)后面.一下,就可以看到很多调用接口,使用对应的接口可以返回不同的内容

//-------------------------------------------------------------------------------
// 直接执行QueryCommand的方式
//-------------------------------------------------------------------------------
//获取数据源——主要用于绑定连接的服务器,如果有多台服务器多个数据库时,可使用不同的数据源来进行绑定查找
var provider = SubSonic.DataProviders.ProviderFactory.GetProvider();
//定义事务,给后面的事务调用
var batch = new BatchQuery(provider);

var sql = string.Format("select * from {0}", BranchTable.TableName);
//例一,获取影响记录数
QueryCommand qcommand = new QueryCommand(sql, provider);
WriteLine(qcommand.Provider.ExecuteQuery(qcommand) + "");

//例二,获取DataTable
var q = new SubSonic.Query.QueryCommand(sql, provider);
var table = q.Provider.ExecuteDataSet(q).Tables[0];
if (dt != null && table.Rows.Count > 0)
{
for (int i = 0; i < table.Rows.Count; i++)
{
//打印到输出窗口
WriteLine(table.Rows[i][BranchTable.Id] + " " + table.Rows[i][BranchTable.Name]);
}
}

//例三,使用事务执行
batch.QueueForTransaction(qcommand);
batch.ExecuteTransaction();

//例四,直接使用数据源执行
provider.ExecuteQuery(qcommand);
//-------------------------------------------------------------------------------

//-------------------------------------------------------------------------------
// Linq查询方式
//-------------------------------------------------------------------------------
//Linq查询方式
var query = new Query<Branch>(db.Provider);
var posts = from p in query
where p.DeptCode.StartsWith("0101")
select p;
foreach (var tem in posts)
{
//打印到输出窗口
WriteLine(tem.ToString());
}

query = db.GetQuery<Branch>();
posts = from p in query
where p.Id > 3 && p.Id < 6
select p;

//获取查询结果1
foreach (var tem in posts)
{
//打印到输出窗口
WriteLine(tem.ToString());
}

//获取查询结果2
List<Branch> li2 = query.ToList<Branch>();
foreach (var tem in li2)
{
//打印到输出窗口
WriteLine(tem.ToString());
}

//-------------------------------------------------------------------------------
// Linq多表关联查询方法
//-------------------------------------------------------------------------------
//方法一
var query5 = from p in Position.All()
join b in Branch.All() on p.Branch_Id equals b.Id
where b.DeptCode == "0101"
select p;
foreach (var tem in query5)
{
//打印到输出窗口
WriteLine(tem.ToString());
}

//方法二
var qry = (from p in db.Position
join b in db.Branch on p.Branch_Id equals b.Id
where b.DeptCode == "0101"
select new ListView
{
PositionName = p.Name,
BranchName = p.Branch_Name,
DeptCode = b.DeptCode
});

foreach (var view in qry)
{
WriteLine(view.ToString());
}

//-------------------------------------------------------------------------------
// 使用事务
//-------------------------------------------------------------------------------
//例一
//从部门表中查询出编号为0102的Id、名称与说明三列
var query1 = new SubSonic.Query.Select(provider, BranchTable.Id, BranchTable.Name, BranchTable.Comment).From(BranchTable.TableName).Where<Branch>(x => x.DeptCode == "0102");
//加入事务
batch.QueueForTransaction(query1);

//查询部门编号为0102且职位名称后面两个字为主管的所有职位
var query2 = new SubSonic.Query.Select(provider).From<Position>().Where<Position>(x => x.Branch_DeptCode == "0102").And(PositionTable.Name).EndsWith("主管");
//加入事务
batch.QueueForTransaction(query2);
//运行事务,不返回任何信息
batch.ExecuteTransaction();

//例二
batch = new BatchQuery();
batch.Queue(query1);
batch.Queue(query2);
//执行事务,并返回数据
using (IDataReader rdr = batch.ExecuteReader())
{
if (rdr.Read())
{
//query1 results
for (int i = 0; i < rdr.FieldCount; i++)
{
WriteLine(rdr[i] + "");
}
}
//rdr.MoveNext();
rdr.NextResult();
if (rdr.Read())
{
//query2 results
for (int i = 0; i < rdr.FieldCount; i++)
{
WriteLine(rdr[i] + "");
}
}
}

//例三
batch = new BatchQuery(provider);

var query3 = from p in db.Branch
where
p.Id > 1 && p.Id < 10
select p;
batch.Queue(query3);

var query4 = from p in db.Position
where p.Branch_DeptCode == "0103"
select p;
batch.Queue(query4);
//执行事务,并返回数据
using (var rdr = batch.ExecuteReader())
{
if (rdr.Read())
{
//query1 results
for (int i = 0; i < rdr.FieldCount; i++)
{
WriteLine(rdr[i] + "");
}
}
//rdr.MoveNext();
rdr.NextResult();
if (rdr.Read())
{
//query2 results
for (int i = 0; i < rdr.FieldCount; i++)
{
WriteLine(rdr[i] + "");
}
}
}

}

/// <summary>
/// DeBug时,在输出窗口打印出指定内容
/// </summary>
/// <param name="str">要在输出窗口显示的内容</param>
private void WriteLine(string str)
{
System.Diagnostics.Debug.WriteLine(str);
}

//内部类
class ListView
{
public string PositionName { get; set; }

public string BranchName { get; set; }

public string DeptCode { get; set; }

public string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("PositionName = " + PositionName + "\r\n");
sb.Append("BranchName = " + BranchName + "\r\n");
sb.Append("DeptCode = " + DeptCode + "\r\n");

return sb.ToString();
}
}
}
}

 版权声明:

  本文由AllEmpty原创并发布于博客园,欢迎转载,未经本人同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。如有问题,可以通过[email protected]
联系我,非常感谢。

  发表本编内容,只要主为了和大家共同学习共同进步,有兴趣的朋友可以加加Q群:327360708
或Email给我([email protected]),大家一起探讨。

  更多内容,敬请观注博客:http://www.cnblogs.com/EmptyFS/

从零开始编写自己的C#框架(6)——SubSonic3.0插件介绍(附源码)

时间: 2024-08-05 19:36:08

从零开始编写自己的C#框架(6)——SubSonic3.0插件介绍(附源码)的相关文章

Android应用经典主界面框架之一:仿QQ (使用Fragment, 附源码)

最近反复研究日常经典必用的几个android app,从主界面带来的交互方式入手进行分析,我将其大致分为三类.今天记录第一种方式,即主界面下面有几个tab页,最上端是标题栏,tab页和tab页之间不是通过滑动切换的,而是通过点击切换tab页.早期这种架构一直是使用tabhost+activitygroup来使用,随着fragment的出现及google官方也大力推荐使用fragment,后者大有代替前者之势.本文也使用fragment进行搭建,标题中的"经典"指这种交互经典,非本文的代

从零开始编写自己的C#框架(9)——数据库设计与创建

对于千万级与百万级数据库设计是有所区别的,由于本项目是基于中小型软件开发框架来设计,记录量相对会比较少,所以数据库设计时考虑的角度是:与开发相结合:空间换性能:空间换开发效率:减少null异常......当然不同的公司与项目要求不同,初学者要学会适应不同的项目开发要求,使用本框架开发时,必须严格按照本章节的要求来设计数据库,不然可能会产生不可控的异常. 从零开始编写自己的C#框架 数据库设计规范   文件状态: [√] 草稿 [  ] 正式发布 [  ] 正在修改 文件标识: C#框架 当前版本

从零开始编写自己的C#框架(1)——前言

原文:从零开始编写自己的C#框架(1)--前言 记得十五年前自学编程时,拿着C语言厚厚的书,想要上机都不知道要用什么编译器来执行书中的例子.十二年前在大学自学ASP时,由于身边没有一位同学和朋友学习这种语言,也只能整天混在图收馆里拼命的啃书.而再后来也差不多,自学了很多不同的知识,都一直只能自己默默的克服一个又一个困难.所以这几年带一些应届生或只有一两年经验的新人时,都会同他们讲:你们现在太幸福了,有问题可以找度娘,还可以找我来解决.做为过来人,能深深的体会到刚入门的时候,没有系统的介绍和老师指

从零开始编写自己的C#框架(14)——T4模板在逻辑层中的应用(三)

原本关于T4模板原想分5个章节详细解说的,不过因为最近比较忙,也不想将整个系列时间拉得太长,所以就将它们整合在一块了,可能会有很多细节没有讲到,希望大家自己对着代码与模板去研究. 本章代码量会比较大,基本将Web层要使用到的大部分函数都用模板生成了出来,而模板中的函数,很多也是互相关联调用的.另外在DotNet.Utilities(公共函数项目)中也添加与修改了一些类和函数. 需要特别说明的是,在逻辑层添加了July大神编写的超强上传类,具体怎么使用功能怎么强大,在后面调用到时会用一个章节详细说

从零开始编写自己的C#框架(5)——三层架构介绍

原文:从零开始编写自己的C#框架(5)--三层架构介绍 三层架构对于开发人员来说,已经是司空见惯了,除了大型与超小型项目外,大多都是这种架构来进行开发. 在这里为初学者们简单介绍一下三层架构: (下面内容摘自<趣味理解:三层架构与养猪-<.NET深入体验与实战精要>>,这是以前看到的关于三层架构介绍,觉得挺经典的,大家有时间的话认真看看) 对比以上两图,我们可以看出: 1)数据库好比猪圈 ,所有的猪有序地按区域或编号,存放在不同的猪栏里. 2)DAL 好比是屠宰场 ,把猪从猪圈取出

从零开始编写自己的C#框架(2)——开发前准备工作

原文:从零开始编写自己的C#框架(2)--开发前准备工作 没想到写了个前言就受到很多朋友的支持,大家的推荐就是我最大的动力(推荐得我热血沸腾,大家就用推荐来猛砸我吧O^-^O),谢谢大家支持. 其实框架开发大家都知道,不过要想写得通俗点,我个人觉得还是挺吃力的.在本系列中,我不会很深入的去讲述那些映射啊关系啊(绕得我头都晕了,这些请大家去查看相关的文档学习吧)......概念上的东西会少讲,直接上实例,讲实际应用.当然如果您觉得这样很不尊重面向对象什么的,那也没办法,只能说我们不在一个频道上.

从零开始编写自己的C#框架(19)——Web层后端权限模块

不知不觉本系统写了快三个月了,最近写页面的具体功能时感觉到有点吃力,很多地方如果张嘴来讲的话可以说得很细,很全面,可写成文字的话,就不太会写了,有些地方想讲得清晰的话,得用多几倍的文字+实例+变化中的图片才能表达得清楚,而写这些又太费时间了,近段时间又特忙,所以只能是尽力而为,希望大家自行研究,如果有什么地方不明白的,发发评论或邮件给我,我再重新详细讲解. 说回正题,对于页面访问权限以及每个按键的权限控制,很久以前用过好几种不同的方法,比如为每个控件分配名称或编码,然后在写代码时绑定这些值,又比

从零开始编写自己的C#框架(17)——Web层后端首页

后端首页是管理员登陆后进入的第一个页面,主要是显示当前登陆用户信息.在线人数.菜单树列表.相关功能按键和系统介绍.让管理员能更方便的找到息想要的内容. 根据不同系统的需要,首页会显示不同的内容,比如显示公司公告.公司新闻.内部短消息.个人事务.各种业务提醒......等各种内容,这些大家可以需要去进行呈现. 先上代码 Main.aspx 1 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind=&qu

从零开始编写自己的C#框架(26)——小结

一直想写个总结,不过实在太忙了,所以一直拖啊拖啊,拖到现在,不过也好,有了这段时间的沉淀,发现自己又有了小小的进步.哈哈...... 原想框架开发的相关开发步骤.文档.代码.功能.部署等都简单的讲过了,就此了结本系列文章,经过这段日子的深入学习,发现本系列文章讲的还是太肤浅了,很多东西都没有讲到,也没有说明白.所以过段时间空闲些了,会继续从理论上来讲解怎么去设计一个框架(也算是给自己定个目标,加加压力),有了前面的代码了解,再学习理论相信大家也更容易接受了. 小结 学习如逆水行舟,不进则退,当能