手工搭建基于ABP的框架(2) - 访问数据库
为了防止不提供原网址的转载,特在这里加上原文链接:
http://www.cnblogs.com/skabyy/p/7517397.html
本篇我们实现数据库的访问。我们将实现两种数据库访问方法来访问一个SQLite数据库——使用NHibernate实现的ORM映射访问和使用Dapper实现的SQL语句访问。然后完成前一篇未完成的CreateTweet
和GetTweets
接口。
在开始之前,先做一些准备工作,新建Domain层的Module:
public class MyTweetDomainModule : AbpModule
{
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
}
同时MyTweetApplicationModule
添加对MyTweetDomainModule
的依赖:
[DependsOn(typeof(MyTweetDomainModule))]
public class MyTweetApplicationModule : AbpModule
安装NuGet包Abp.NHibernate
到MyTweet.Domain
和MyTweet.Infrastructure
。
下面我们将完成这些步骤来实现数据库的访问:
- 配置数据库连接
- 新建
tweet
表以及相应的Model类型 - 实现访问数据的Repository
- 用Dapper实现通过SQL访问数据库
使用Fluent NHibernate配置数据库连接
我们这里使用的数据库是SQLite数据库,其他数据库的配置也是类似的。我们将连接到App_Data
文件夹下的一个SQLite数据库。新建LocalDbSessionProvider
类并在构造函数处配置数据库连接。由于LocalDbSessionProvider
实现了接口ISingletonDependency
,模块初始化时LocalDbSessionProvider
会以单例的形式注册到IoC容器。
public class LocalDbSessionProvider : ISessionProvider, ISingletonDependency, IDisposable
{
protected FluentConfiguration FluentConfiguration { get; private set; }
private ISessionFactory _sessionFactory;
public LocalDbSessionProvider()
{
FluentConfiguration = Fluently.Configure();
// 数据库连接串
var connString = "data source=|DataDirectory|MySQLite.db;";
FluentConfiguration
// 配置连接串
.Database(SQLiteConfiguration.Standard.ConnectionString(connString))
// 配置ORM
.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()));
// 生成session factory
_sessionFactory = FluentConfiguration.BuildSessionFactory();
}
private ISession _session;
public ISession Session
{
get
{
if (_session != null)
{
// 每次访问都flush上一个session。这里有效率和多线程问题,暂且这样用,后面会改。
_session.Flush();
_session.Dispose();
}
_session = _sessionFactory.OpenSession();
return _session;
}
}
public void Dispose()
{
_sessionFactory.Dispose();
}
}
这里每次用到session都只是简单地把上一次的session flush
了,然后打开新的session。这会有效率和多线程冲突的问题。这里只是单纯为了展示实现数据库链接的方法而先用的简单实现。后面做工作单元(UoW)时会解决这个问题。
为了NHibernate能创建SQLite的连接,还需要安装System.Data.SQLite.Core
到MyTweet.Web
(其他数据库的话要安装其他相应的包)。
新建tweet
表以及相应的Model类型
我们用tweet
表保存tweet数据。tweet数据表接口以及对应Model属性如下:
数据库字段 | Model属性 | 类型 | 描述 |
---|---|---|---|
pk_id |
PkId |
string |
主键 |
content |
Content |
string |
内容 |
create_time |
CreateTime |
string |
创建时间 |
使用SQLite工具新建MySQLite.db文件,并新建表tweet
。
然后将MySQLite.db文件拷贝到App_Data
文件夹下。
CREATE TABLE `tweet` (
`pk_id` TEXT,
`content` TEXT,
`create_time` TEXT NOT NULL,
PRIMARY KEY(`pk_id`)
);
接下来新建Model类Tweet
以及映射TweetMapper
。Tweet
继承Entity<string>
,其中的string
表示Tweet
的主键Id
是string
类型的。TweetMapper
继承ClassMap<Tweet>
,上面LocalDbSessionProvider
构造函数执行到.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
这个方法时,会用反射的方式搜索程序集中ClassMap<T>
的子类,建立Model和数据库表的映射(Tweet
和tweet
表的映射)。
public class Tweet : Entity<string> // 主键为string类型
{
public string Content { get; set; }
public DateTime CreateTime { get; set; }
}
public class TweetMapper : ClassMap<Tweet>
{
public TweetMapper()
{
// 禁用惰性加载
Not.LazyLoad();
// 映射到表tweet
Table("tweet");
// 主键映射
Id(x => x.Id).Column("pk_id");
// 字段映射
Map(x => x.Content).Column("content");
Map(x => x.CreateTime).Column("create_time");
}
}
实现Repository与增查接口
Repository即是DDD中的仓储,它封装了数据对象的增删改查操作。ABP的NhRepositoryBase
已经实现了常用的增删改查功能,因此这里只需要继承一下就行了。
public interface ITweetRepository : IRepository<Tweet, string> { }
public class TweetRepository : NhRepositoryBase<Tweet, string>, ITweetRepository
{
public TweetRepository()
: base(IocManager.Instance.Resolve<LocalDbSessionProvider>())
{ }
}
最后,修改MyTweetAppService
,实现CreateTweet
接口和GetTweets
接口。
public class CreateTweetInput
{
public string Content { get; set; }
}
public class MyTweetAppService : ApplicationService, IMyTweetAppService
{
public ITweetRepository TweetRepository { get; set; }
public object GetTweets(string msg)
{
return TweetRepository.GetAll().OrderByDescending(x => x.CreateTime).ToList();
}
public object CreateTweet(CreateTweetInput input)
{
var tweet = new Tweet
{
Id = Guid.NewGuid().ToString("N"),
Content = input.Content,
CreateTime = DateTime.Now
};
var o = TweetRepository.Insert(tweet);
return o;
}
}
大功告成!测试一下。用Postman调用CreateTweet
接口插入一条tweet:
然后调用GetTweets
查询:
ABP的依赖注入
可能有同学会疑惑,在MyTweetAppService
中只声明了ITweetRepository
类型的属性TweetRepository
,但是并没有进行赋值,那么这个属性的对象实例是哪里来的呢?这就涉及到ABP框架的依赖注入策略了。
ABP基于Castle Windsor框架实现自己的依赖注入功能。依赖注入最基本的功能无非是注册(Register
)和解析(Resolve
)两个,注册功能将对象注册到IoC容器,解析功能根据类名或接口名获从IoC容器获取已注册的对象。我们可以直接通过IocManager
获得Castle Windsor的IoC容器,直接进行注册和解析操作。
// 以单例模式注册类型T
IocManager.Register<T>(Abp.Dependency.DependencyLifeStyle.Singleton);
// 以临时对象模式注册类型T,解析的时候会生成T的一个新对象
IocManager.Register<T>(Abp.Dependency.DependencyLifeStyle.Transient);
// 从IoC容器解析已注册的类型T的对象
var obj = IocManager.Resolve<T>();
还有一些其他方法可以做注册和解析,具体可以参照ABP的文档。不过一般都不需要使用这些方法。ABP框架有一套依赖注入的规则,通过编写应用程序时遵循最佳实践和一些约定,使得依赖注入对于开发者几乎是透明的。
ABP的注册
基本上每个模块的初始化方法都会有这么一行代码:
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
模块初始化时,ABP会搜索这个模块所在的程序集,自动注册满足常规注册条件与实现帮助接口的类。
常规注册
ABP自动注册所有Repositories, Domain Services, Application Services, MVC 控制器和Web API控制器。ABP通过判断是否实现了相应接口来判断是不是上述几类。例如下面的MyAppService
:
public interface IMyAppService : IApplicationService { }
public class MyAppService : IMyAppService { }
由于它实现了接口IApplicationService
,ABP会自动注册,我们就可以通过IMyAppService
解析出一个MyAppService
对象。
通过常规注册的类的生命期都是transient(临时的),每次解析时都会生成一个新的临时对象。
帮助接口
ABP另外提供了ITransientDependency
和ISingletonDependency
两个接口。这两个接口前面也有用到过了。实现了ITransientDependency
的类会被注册为transient。而实现了ISingletonDependency
的类则被注册为单例。
ABP的解析
除了手工解析外,还可以通过构造函数和公共属性注入来获取类的依赖。这也是最常用的方法。例如:
public class MyAppService : IMyAppService
{
public ILogger Logger { get; set; }
private IMyRepository _repo;
public MyAppService(IMyRepository repo)
{
_repo = repo;
}
}
ILogger
从公共属性注入,IMyRepository
从构造函数注入。注入过程对开发者是透明的,开发者不需要去写注入的代码。
QueryService - 使用SQL语句查询数据
实际开发中,经常需要直接使用SQL进行数据访问。查询逻辑比较复杂时直接使用SQL可以避免复杂的Mapper。通常复杂的Mapper会导致低效率的查询甚至会触发NHibernate一些奇怪的bug。实际上,在开发中,对于单纯的读取数据的功能(即使查询逻辑不复杂),我们建议直接使用SQL查询实现。直接使用SQL查询在调试时更为方便——直接拷贝SQL语句到SQL客户端执行即可检验该语句是否正确。
下面简要介绍一下使用Dapper来实现数据库查询功能。封装了sql查询操作的类我们称为QueryService。
首先,安装dapper
包到MyTweet.Infrastructure
。在MyTweet.Infrastructure
实现QueryService的基类BaseQueryService
:
public class BaseQueryService : ITransientDependency
{
private ISessionProvider _sessionProvider;
protected BaseQueryService(ISessionProvider sessionProvider)
{
_sessionProvider = sessionProvider;
}
public IEnumerable<T> Query<T>(string sql, object param = null)
{
var conn = _sessionProvider.Session.Connection;
return conn.Query<T>(sql, param);
}
}
Dapper给System.Data.IDbConnection
接口扩展了Query<T>
方法,该方法执行SQL查询并将查询结构映射为IEnumerable<T>
类型的对象。为了使用这个扩展方法,还需在文件开头加个using
语句。
using Dapper;
QueryService并不在ABP依赖注入的常规注册规则里,所以让BaseQueryService
实现了ITransientDependency
,这样它的子类都会自动被注册到IoC容器。
接下来在MyTweet.Domain
新建类TweetQueryService
,它负责实现具体的SQL查询。方法SearchTweets
实现了查询包含关键词keyword
的所有tweet。
public interface ITweetQueryService
{
IList<Tweet> SearchTweets(string keyword);
}
public class TweetQueryService : BaseQueryService, ITweetQueryService
{
public TweetQueryService() : base(IocManager.Instance.Resolve<LocalDbSessionProvider>())
{ }
public IList<Tweet> SearchTweets(string keyword)
{
var sql = @"select
pk_id Id,
content Content,
create_time CreateTime
from tweet
where content like '%' || @Keyword || '%'";
return Query<Tweet>(sql, new { Keyword = keyword ?? "" }).ToList();
}
}
最后在MyTweetAppService
实现查询tweet数据的接口GetTweetsFromQS
:
public ITweetQueryService TweetQueryService { get; set; }
public object GetTweetsFromQS(string keyword)
{
return TweetQueryService.SearchTweets(keyword);
}
测试一下:
结束
本文介绍了通过NHibernate以及Dapper进行数据库访问的方法,简单说明了ABP依赖注入策略。现在数据库连接部分的代码只是单纯为了演示的简单实现,没有做合理的数据库Session管理,会有效率和多线程冲突的问题。后面会加上工作单元(Unit of Work)来解决这些问题。
最后,放上代码链接:https://github.com/sKabYY/MyTweet-AbpDemo
手工搭建基于ABP的框架(2) - 访问数据库的更多相关文章
- 手工搭建基于ABP的框架 - 工作单元以及事务管理
一个业务功能往往不只由一次数据库请求(或者服务调用)实现.为了功能的完整性,我们希望如果该功能执行一半时出错,则撤销前面已执行的改动.在数据库层面上,事务管理实现了这种完整性需求.在ABP中,一个完整 ...
- 手工搭建基于ABP的框架(3) - 登录,权限控制与日志
为了防止不提供原网址的转载,特在这里加上原文链接: http://www.cnblogs.com/skabyy/p/7695258.html 本篇将实现登录.权限控制.日志配置与审计日志的功能.首先我 ...
- phoenix 使用activerecord模式框架ecto 访问数据库
备注: 需要先安装mysql 以及phoenix 框架,测试使用的是docker 进行安装,具可以参考代码 1. 创建项目 mix phx.new first --database mys ...
- ABP框架用Dapper实现通过SQL访问数据库
ABP的框架(2) - 访问数据库 为了防止不提供原网址的转载,特在这里加上原文链接:http://www.cnblogs.com/skabyy/p/7517397.html 本篇我们实现数据库的 ...
- 手工搭建ABP框架(1) - Web项目
为了防止不提供原网址的转载,特在这里加上原文链接: http://www.cnblogs.com/skabyy/p/7295533.html ABP是 ASP.NET Boilerplate Proj ...
- ABP架构学习系列三:手工搭建ABP框架
由于公司的项目才接触到ABP这个框架,当时就觉得高大上,什么IOC.AOP.ddd各种专业词汇让人激情 澎湃,但在使用过程中碰到了许多坑,可能也许是没有去看源码导致的,但工作确实没有那么多时间让人去慢 ...
- 第二篇 基于.net搭建热插拔式web框架(沙箱的构建)
上周五写了一个实现原理篇,在评论中看到有朋友也遇到了我的问题,真的是有种他乡遇知己的感觉,整个系列我一定会坚持写完,并在最后把代码开源到git中.上一篇文章很多人看了以后,都表示不解,觉得不知道我到底 ...
- 基于.net搭建热插拔式web框架(实现原理)
第一节:我们为什么需要一个热插拔式的web框架? 模块之间独立开发 假设我们要做一个后台管理系统,其中包括“用户活跃度”.“产品管理”."账单管理"等模块.每个模块中有自己的业务特 ...
- ABP从入门到精通(5):使用基于JWT标准的Token访问WebApi
项目:asp.net zero 4.2.0 .net core(1.1) 版本 我们做项目的时候可能会遇到需要提供api给app调用,ABP动态生成的WebApi提供了方便的基于JWT标准的Token ...
随机推荐
- cs224d 自然语言处理作业 problem set3 (一) 实现Recursive Nerual Net Work 递归神经网络
1.Recursive Nerual Networks能够更好地体现每个词与词之间语法上的联系这里我们选取的损失函数仍然是交叉熵函数 2.整个网络的结构如下图所示: 每个参数的更新时的梯队值如何计算, ...
- spring框架总结(01)
1.spring是什么? sprint其实就是一个开源框架,是于2003年兴起的一个轻量级的java开发框架,是有Road Johnson创建的,简单的来说spring是一个分层的JavaSE/EE( ...
- 【归纳整理】Ajax / JSON / WEB存储 / iframe
Ajax 一.什么是 AJAX ? AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML). AJAX 是一种用于创建快速动态网页 ...
- 原创:工作指派问题解决方案---模拟退火算法C实现
本文忽略了对于模拟退火的算法的理论讲解,读者可参考相关的博文或者其他相关资料,本文着重于算法的实现: /************************************************ ...
- FPGA在电平接口领域的应用
电子技术的发展,产生了各种各样的电平接口. TTL电平: TTL电平信号之所以被广泛使用,原因是因为:通常我们采用二进制来表示数据.而且规定,+5V等价于逻辑"1",0V等价于逻辑 ...
- 常用硬件设备GUID
Class GUID Device Description CDROM 4D36E965-E325-11CE-BFC1-08002BE10318 CD/DVD/Blu-ray drives DiskD ...
- python的urlparse
urlparse主要是URL的分解和拼接,分析出URL中的各项参数,可以被其他的URL使用. 主要的函数有: 1.urlparse 将URL分解为6个片段,返回一个元组,包括协议.基地址.相对地址等等 ...
- 怎样让PDM图形列表显示name和code等需要的信息
1. 工具(TOOLS)-〉显示参数设置(DISPLAY PREFERENCES) 2. 在弹出来的框中选中Content-〉Table 3. 点右下角那个Advanced 按钮 4. 在弹出的框个选 ...
- 正则表达式与grep和sed
正则表达式与grep和sed 目录 1.正则表达式 2.grep 3.sed grep和sed需要正则表达式,我们需要注意的正则表达式与通配符用法的区分. 1.正则表达式 REGEXP,正则表达式:由 ...
- 关于使用连接器arm-linux-ld时指定链接地址的作用
首先,记住一句话:程序的链接地址必须等于运行地址! 在学习exynos 4412的启动过程时,发现自己对链接地址的作用不是很了解,于是上网查找了资料做了基本了解,在此做个总结. 上图是exynos 4 ...