动态代理仓储

SmartSql源码:https://github.com/Ahoo-Wang/SmartSql

简介

动态代理仓储(SmartSql.DyRepository)组件是SmartSql非常独特的功能,它能简化SmartSql的使用。对业务代码除了配置几乎没有侵入。可以说使用SmartSqlContainer是原始方法,而DyRepository自动帮你实现这些方法。

DyRepository的表现是只需要定义仓储接口,通过简单配置就能自动实现这些接口并注册到IoC容器中,使用时注入即刻获取实现。原理是通过接口和接口方法的命名规则来获取SmartSql的xml文件中的Scope和SqlId,用接口方法的参数作为Request,通过xml中的sql自动判断是查询还是执行操作,最后实现对ISmartSqlMapper的调用。

适合场景

  1. 使用了仓储模式的架构

仓储模式主要在DDD战术中运用,用来隔离领域和数据库。DyRepository的功能需求主要是在DDD的实践中发现的,目前为止已经满足DDD实践的大部分需求,如果还有其他的相关需求欢迎提出Issue。

  1. 类似SqlHepler的应用

DyRepository可以将任意一个接口实现出查询数据库的工具,CURD方法不在话下。通过接口注入更能发挥解耦的作用。

使用介绍

下面会简单演示DyRepository与ISmartSqlMapper的使用对比。

准备工作

  1. 先创建一个仓储,这个仓储不依赖SmartSql,只是普普通通的仓储接口
    //仓储接口,默认模版是I{Scope}Repository,所以这个接口的Scope是Activity
public interface IActivityRepository
{
//接口方法对应SqlId,所以这个方法的SqlId是Insert
//方法参数对应Request,所以这个方法的Request是activity
int Insert(Activity activity); //值类型的参数会自动封装为一个对象,所以这个方法的Request是new { activityId = activityId }
Activity Query(long activityId);
}
  1. 创建配置xml文件SmartSqlMapConfig.xml:
<?xml version="1.0" encoding="utf-8" ?>
<SmartSqlMapConfig xmlns="http://SmartSql.net/schemas/SmartSqlMapConfig.xsd">
<Settings IsWatchConfigFile="true" IgnoreParameterCase="true"/>
<Database>
<!--ParameterPrefix:[SqlServer:@ | MySQL:? |Oracle::] -->
<DbProvider Name="SqlClientFactory" ParameterPrefix="@" Type="System.Data.SqlClient.SqlClientFactory,System.Data.SqlClient"/>
<Write Name="WriteDB" ConnectionString="Data Source=.;Initial Catalog=SmartSqlStarterDB;Integrated Security=True"/>
</Database>
<SmartSqlMaps>
<SmartSqlMap Path="Maps" Type="Directory"></SmartSqlMap>
</SmartSqlMaps>
</SmartSqlMapConfig>
  1. 再创建xml配置文件Activity.xml,放到Maps目录,并且在属性面板设置为“始终复制”:
<?xml version="1.0" encoding="utf-8" ?>
<SmartSqlMap Scope="Activity" xmlns="http://SmartSql.net/schemas/SmartSqlMap.xsd">
<Statements>
<Statement Id="Insert">
INSERT INTO Activity
(ActivityId
,Name
,BeginTime
,Address
,CreationTime
,Deleted)
VALUES
(@ActivityId
,@Name
,@BeginTime
,@Address
,@CreationTime
,@Deleted)
;Select Scope_Identity();
</Statement> <Statement Id="Query">
SELECT * FROM Activity
WHERE
ActivityId = @activityId;
</Statement>
</Statements>
</SmartSqlMap>

准备工作完成,下面就可以展示两种用法的区别。

两种用法

ISmartSqlMapper 用法

如果不用DyRepository,我们需要用ISmartSqlMapper实现这个仓储。


public class ActivityRepository : IActivityRepository
{
ISmartSqlMapper SqlMapper = MapperContainer.Instance.GetSqlMapper(); public int Insert(Activity activity)
{
return SqlMapper.ExecuteScalar<int>(new RequestContext()
{
Scope = "Activity",
SqlId = "Insert",
Request = activity
});
} public Activity Query(long activityId)
{
return SqlMapper.Query<Activity>(new RequestContext()
{
Scope = "Activity",
SqlId = "Query",
Request = new { activityId = activityId }
});
}
}

再把实现类注册到IoC中:

var services = new ServiceCollection();
var services.AddSingleton<IActivityRepository,ActivityRepository>();

DyRepository用法

如果使用DyRepository,我们不需要再写接口实现,只需配置一下IoC注册即可。

    var services = new ServiceCollection();
services.AddSmartSqlRepositoryFromAssembly((options) =>
{
options.AssemblyString = "SmartSql.Starter.Repository";
});

注入使用

使用方法就注入接口,再调用接口方法了。

    // 假设ActivityService已经注册到IoC容器
public class ActivityService
{
IActivityRepository activityRepository; public ActivityService(IActivityRepository activityRepository)
{
this.activityRepository = activityRepository;
} public int Create(Activity activity)
{
return activityRepository.Insert(activity);
} public int GetById(int id)
{
return activityRepository.Insert(id);
}
}

总结

通过DyRepository与ISmartSqlMapper的简单对比,我们就可以看出DyRepository的强大,为我们省下了很多代码。当然,ISmartSqlMapper自然也有它的灵活性,能够在任何地方使用。但是如果没有其他的特殊需求,在架构方面,因为对业务代码几乎无侵入,DyRepository无疑是最推荐的使用方式。

本文只介绍了DyRepository默认约定的使用方法,其实它还能通过各种配置项去实现更灵活的功能。详情请看下一节《DyRepository配置》。

DyRepository配置

DyRepository的配置分为默认配置、特性配置和注册配置,但是都必须配置IoC注册,因为要都需要创建动态的接口实现到IoC中。

必须的配置:

  1. 单个注册
    services.AddRepository<IUserRepository>();
  1. 批量注册
    services.AddSmartSqlRepositoryFromAssembly((options) =>
{
//仓储接口所在程序集全名
options.AssemblyString = "SmartSql.Starter.Repository";
});

可选配置

特性配置指在接口上标注特性来配置DyRepository的配置项,而注册配置是指在IoC注册方法中配置,下面演示一下两者的不同。

Scope配置

场景

I{Scope}Repository是默认配置的Scope模版,如IUserRepository的Scope就是User。如果是这样的接口命名风格则无需再配置。

而当需要换接口命名风格,如查询User的Dao层名称是IUserDao,则需要配置对应的Scope。

特性配置

    [SqlMap(Scope = "User")]
public interface IUserDao
{
}

注册配置

    //接口还是那个接口
public interface IUserDao
{
} //IoC配置中,注册单个接口
services.AddRepository<IUserDao>(scope:"User");
//或批量注册
services.AddSmartSqlRepositoryFromAssembly((options) =>
{
options.AssemblyString = "SmartSql.Starter.Repository";
options.ScopeTemplate = "I{Scope}Dao";
});

注意,AddSmartSqlRepositoryFromAssembly是可以配置多次的,只要被扫描到的接口不同,就可以给不同的接口配置不同的属性

SqlId配置

SqlId默认是取仓储接口的方法名,只要方法名跟xml中的SqlId一样,则无需任何配置。

场景

因为SmartSql的sql配置是可以动态渲染的,当同一个SqlId传入不同的参数,可以渲染出不同的查询条件。例如:

    <Statement Id="Query">
SELECT * FROM User T
<Where>
<IsNotEmpty Prepend="And" Property="Email">
T.Email = @Email
</IsNotEmpty>
<IsNotEmpty Prepend="And" Property="UserName">
T.UserName Like Concat('%',$UserName,'%')
</IsNotEmpty>
</Where>
</Statement>

此时如果只用默认配置,写两个Query(string)方法就会有同方法名同参数类型而无法重载的问题。因此,此时需要接口方法名不同,而通过配置去指定相同的SqlId。

特性配置

    [SqlMap(Scope = "User")]
public interface IUserRepository
{
[Statement(Id = "Query")]
int QueryByEmail(string email); [Statement(Id = "Query")]
int QueryByUserName(string userName);
}

注册配置

注册配置中是通过配置一个叫sqlIdNamingConvert的委托参数来实现接口方法名到SqlId的转换方法。

    services.AddSmartSqlRepositoryFactory(sqlIdNamingConvert: (type, method) =>
{
if (method.Name.StartsWith("QueryBy"))
return "Query"; //返回的就是SqlId
});
services.AddRepositoryFromAssembly((options) =>
{
options.AssemblyString = "SmartSql.Starter.Repository";
})

需要注意的是,这个配置需要把AddSmartSqlRepositoryFactory和AddRepositoryFromAssembly两个方法分开,原因是前几个配置中的AddSmartSqlRepositoryFromAssembly方法内部调用过AddSmartSqlRepositoryFactory,如果再次调用会造成冲突。

其它配置

场景

如果希望SmartSql只做接口实现而不侵入接口,以上的注册配置基本就能满足大部分需求。

但是如果需要深入使用SmartSql,那么利用特性配置和一个泛型接口能得到更多额外的功能。

接口方法指定Sql

即直接给接口方法绑定sql,无需再从xml中配置sql了,但请注意参数前缀还是需要在对应的配置文件配置。

    [Statement(Sql = "Select Top(@taken) T.* From User T With(NoLock);")]
IEnumerable<User> QueryBySql(int taken);

Statement特性只标记在方法上,还有其他几个参数:

参数 默认值 说明
Scope 当前接口的Scope 对应xml的Scope
Id 方法名 xml对应Statement的Id
Execute ExecuteBehavior.Auto 执行类型,一般默认就好
Sql 配置Sql后会直接执行这个特性上的Sql

指定查询参数

即把接口方法的参数值传递给Sql渲染时指定参数名的参数,例如把id的值传递给@UserId:

    IEnumerable<User> Query([Param("UserId")]int id);

泛型接口

继承泛型接口之后,能够直接调用它里面的CURD通用方法。

  1. 同步调用:IRepository<TEntity, TPrimary>
  2. 异步调用:IRepositoryAsync<TEntity, TPrimary>

SmartSql 动态仓储的更多相关文章

  1. SmartSql 动态代理仓储

    SmartSql 动态代理仓储,一个高生产力的组件.该组件看似很难懂,实际上仅做了映射Statement,转发请求的功能.但却意义重大. SmartSql提供了一个通用泛型仓储接口 SmartSql. ...

  2. SmartSql使用教程(2)——使用动态代理实现CURD

    一.引言 接着上一篇的教程,本章我们继续讲SmartSql.今天的主题是动态仓储. 老规矩,先上一个项目结构 从第二章开始.我们将原来的单一项目做了一个分离.方便之后的更新. 在这个结构中.原本上一章 ...

  3. 你必须知道的 SmartSql !

    介绍 SmartSql = MyBatis + Cache(Memory | Redis) + R/W Splitting +Dynamic Repository + Diagnostics .... ...

  4. SmartSql使用教程(3)——SmartSql中的事务,及AOP的使用

    一.引言 经过两章的铺垫,我们现在对SmartSql已经有了一定的了解,那么今天我们的主题是事务处理.事务处理是常用的一种特性,而SmartSql至少提供了两种使用事务的方法.一种是通过Reposit ...

  5. SmartSql使用教程(4)——多库配置与使用

    一.引言 已经几个月没更新了.本来上一章的预告是准备写TypeHandler的相关特性的.但是在准备的时候.SmartSql的作者重构了一下TypeHandler,使得我一下子没搞懂TypeHandl ...

  6. CQRS学习——Storage实现(EF+Code First+DynamicReponsitory)[其四]

    [这里是的实现,指的是针对各个数据访问框架的一个基础实现] 目标 定义仓储/QueryEntry的基本功能 实现仓储的基本功能,以利于复用 实现一些常用的功能 提供一些便利的功能 目标框架 博主使用的 ...

  7. SmartSql 介绍

    介绍 SmartSql = MyBatis + Cache(Memory | Redis) + R/W Splitting +Dynamic Repository + Diagnostics .... ...

  8. SmartSql V3 重磅发布

    超轻量级的ORM框架!107kb 更新内容 移除Dapper依赖 支持存储过程 增强扩展性 重构代码 优化缓存策略 动态实现仓储接口 支持 参数&结果映射 & TypeHandler ...

  9. .Net Core MVC 网站开发(Ninesky) 2.3、项目架构调整(续)-使用配置文件动态注入

    上次实现了依赖注入,但是web项目必须要引用业务逻辑层和数据存储层的实现,项目解耦并不完全:另一方面,要同时注入业务逻辑层和数据访问层,注入的服务直接写在Startup中显得非常臃肿.理想的方式是,w ...

随机推荐

  1. mac配置jenkins遇到的问题及解决办法

    写这篇博客的时候,我暂时放弃了mac配置jenkins,先记着遇到的坑吧.虽然无数次想砸电脑,但是回头想想,对于经常用windows系统和接触过linux的测试的我来说,这也是个熟悉mac系统的机会. ...

  2. ClipboardJS的坑,

    new 一下构造函数就出错了,为什么...Uncaught TypeError: Cannot read property 'addEventListener' of nullat r (clipbo ...

  3. Angular内提供了一个可以快速建立测试用web服务的方法:内存 (in-memory) 服务器

    如何使用 Angular 内存 (in-memory) 服务器https://segmentfault.com/a/1190000009898540

  4. 在Eclipse中快速添加main方法

    方法一: 在创建类时自动添加,勾选“public static void main(String[]   args)” 方法二: 输入main之后按"alt+/"组合键,选择如图所 ...

  5. selenium的三种等待时间设置

    为了提高脚本的稳定性,我们需要在脚本中增加等待时间 第一种:强制等待 Thread.sleep():固定休眠时间设置,Java的Thread类里提供了休眠方法sleep,导入包后就能使用 sleep( ...

  6. Codeforces Round #412

    第一题水题,8分钟1a #include<map> #include<set> #include<cmath> #include<queue> #inc ...

  7. UVA-10972 RevolC FaeLoN (边双连通+缩点)

    题目大意:将n个点,m条边的无向图变成强连通图,最少需要加几条有向边. 题目分析:所谓强连通,就是无向图中任意两点可互达.找出所有的边连通分量,每一个边连通分量都是强连通的,那么缩点得到bcc图,只需 ...

  8. http协议code码

    301 永久重定向 类似手机呼叫转移 302 临时重定向 类似手机呼叫转移 403 forbidden ngnix怎么解决? 含义:表示你在请求一个资源文件,但是nginx不允许你查看. 原因1:访问 ...

  9. nginx和php-fpm的启停和配置

    一.nginx的启停 (1) 启动nginx /etc/init.d/nginx start (2) 停止nginx /etc/init.d/nginx stop (3) 重启nginx /etc/i ...

  10. 【hive】解析url格式字符串

    解析url格式字符串 parse_url() parse_url(url_str,’xxx’):第一个参数是url格式字符串,第二个参数为要解析出来的属性 parse_url(‘http://face ...