• SmartSql 动态仓储


    动态代理仓储

    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>
  • 相关阅读:
    避免scrollview内部控件输入时被键盘遮挡,监听键盘弹起,配合做滚动
    红包功能的开发总结
    App启动时间分析
    第三方动画库 Lottie嵌入记录
    加入一个新的团队需要做什么
    OC 面向对象的特性
    为什么说OC是运行时语言?什么是动态类型、动态绑定、动态加载?
    adb pull 和 adb push
    《重构:改善既有代码的设计》重构的方法整理
    《重构:改善既有代码的设计》(二) 代码的坏味道
  • 原文地址:https://www.cnblogs.com/ElderJames/p/9670296.html
Copyright © 2020-2023  润新知