ABP的框架(2) - 访问数据库

 

为了防止不提供原网址的转载,特在这里加上原文链接:
http://www.cnblogs.com/skabyy/p/7517397.html

本篇我们实现数据库的访问。我们将实现两种数据库访问方法来访问一个SQLite数据库——使用NHibernate实现的ORM映射访问和使用Dapper实现的SQL语句访问。然后完成前一篇未完成的CreateTweetGetTweets接口。

在开始之前,先做一些准备工作,新建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.NHibernateMyTweet.DomainMyTweet.Infrastructure

下面我们将完成这些步骤来实现数据库的访问:

  1. 配置数据库连接
  2. 新建tweet表以及相应的Model类型
  3. 实现访问数据的Repository
  4. 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.CoreMyTweet.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以及映射TweetMapperTweet继承Entity<string>,其中的string表示Tweet的主键Idstring类型的。TweetMapper继承ClassMap<Tweet>,上面LocalDbSessionProvider构造函数执行到.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))这个方法时,会用反射的方式搜索程序集中ClassMap<T>的子类,建立Model和数据库表的映射(Tweettweet表的映射)。

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另外提供了ITransientDependencyISingletonDependency两个接口。这两个接口前面也有用到过了。实现了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框架用Dapper实现通过SQL访问数据库的更多相关文章

  1. 如何用asp.net MVC框架、highChart库从sql server数据库获取数据动态生成柱状图

    如何用asp.net MVC框架.highChart库从sql server数据库获取数据动态生成柱状图?效果大概是这样的,如图: 请问大侠这个这么实现呢?

  2. ABP框架源码学习之修改默认数据库表前缀或表名称

    ABP框架源码学习之修改默认数据库表前缀或表名称 1,源码 namespace Abp.Zero.EntityFramework { /// <summary> /// Extension ...

  3. ABP框架 将EntityFrameworkCore生成的SQL语句输出到控制台

    首先 在 EntityFrameworkCore中安装 Microsoft.Extensions.Logging.Console nuget install Microsoft.Extensions. ...

  4. Dapper 封装oracle底层访问数据库

    如下代码,修改成只支持oracle: using System; using System.Collections.Generic; using System.Data; using System.L ...

  5. 手工搭建基于ABP的框架(2) - 访问数据库

    为了防止不提供原网址的转载,特在这里加上原文链接: http://www.cnblogs.com/skabyy/p/7517397.html 本篇我们实现数据库的访问.我们将实现两种数据库访问方法来访 ...

  6. 使用代码生成工具快速开发ABP框架项目

    在一般系统开发中,我们一般要借助于高度定制化的代码生成工具,用于统一代码风,节省开发时间,提高开发效率.不同的项目,它的项目不同分层的基类定义不同,我们需要在框架基类的基础上扩展我们的业务类代码,尽量 ...

  7. 中小研发团队架构实践之生产环境诊断工具WinDbg 三分钟学会.NET微服务之Polly 使用.Net Core+IView+Vue集成上传图片功能 Fiddler原理~知多少? ABP框架(asp.net core 2.X+Vue)模板项目学习之路(一) C#程序中设置全局代理(Global Proxy) WCF 4.0 使用说明 如何在IIS上发布,并能正常访问

    中小研发团队架构实践之生产环境诊断工具WinDbg 生产环境偶尔会出现一些异常问题,WinDbg或GDB是解决此类问题的利器.调试工具WinDbg如同医生的听诊器,是系统生病时做问题诊断的逆向分析工具 ...

  8. 使用Dapper访问SQL Server数据库

    对应Demo程序名:DapperDemo 准备工作:为项目安装Dapper类库 方法一:项目中添加:项目名右键:Manage NuGet Packages:搜索Dappe:点击安装 方法二:在NuGe ...

  9. ABP框架之——数据访问基础架构(下)

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的一块垫脚石,我们一起精进. EF Core集成 EF Core是微软的ORM,可以使用它与主流的数据库提供商 ...

随机推荐

  1. BZOJ1023:[SHOI2008]cactus仙人掌图(圆方树,DP,单调队列)

    Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌图(cactus). 所谓简单回路就是指在图上不重复经过任何一个顶点 ...

  2. Java中关于AbstractQueuedSynchronizer的入门(一)

    备注:博文仅仅是学习过程中的零散记录,后期整理. AbstractQueuedSynchronizer的简单介绍可以网上搜索,简单了解字段作用. 示例代码,分析获取锁的过程: import java. ...

  3. ng-cordova和cordova区别

    1.cordova介绍    Cordova提供了一组设备相关的API,通过这组API,移动应用能够以JavaScript访问原生的设备功能,如摄像头.麦克风等.    Cordova支持如下7种移动 ...

  4. shell编程之转义和引用

    shell中有两类字符,一类是普通字符,在Shell中除了本身的字面意思外没有其他特殊意义,即普通纯文本:另一类即元字符,是Shell的保留字符,在Shell中有着特殊的含义. 一.转义 转义是指使用 ...

  5. nargin与varargin的用法

    nargin是用来判断输入变量个数的函数,这样就可以针对不同的情况执行不同的功能.通常可以用它来设定一些默认值.如下例所示: 函数文件 examp.m function fout=examp(a,b, ...

  6. C# 语法五 单例类、单例模式

    1.优点 只有一个实例 2.缺点 a)这个实例不能随时释放掉,占用资源. b)每次使用,都要判断是否为空,增加消耗 3.适用场景 只能有一个实例的业务场景,例如:数据库连接对象(每次连接都是同一个连接 ...

  7. ubuntu RPLIDAR A2的使用

    RPLIDAR是由RoboPeak Team,SlamTec公司开发的低成本2D LIDAR解决方案.它可以扫描6度半径内的360°环境. RPLIDAR的输出非常适合构建地图,做slam或构建3D模 ...

  8. kubernetes 里面pod时间修改

    yaml文件中设置时区同步,只需要映射主机的“/etc/localtime”文件. apiVersion: extensions/v1beta1kind: Deploymentmetadata: na ...

  9. 如何屏蔽SkylineGlobe提供的三维地图控件上的快捷键

    SkyllineGlobe提供的 <OBJECT ID=" TerraExplorer3DWindow" CLASSID="CLSID:3a4f9192-65a8- ...

  10. 九,ESP8266 判断是断电上电(强制硬件复位)之后运行的内部程序还是内部软件复位之后运行的程序(基于Lua脚本语言)

    现在我有一个需求,WIFI模块控制一个继电器,我要做的是如果内部程序跑乱了,造成了内部程序复位重启,那么控制继电器的状态不能改变 如果是设备断电了,然后又来电了,我需要的是继电器一定要是断开才好.不能 ...