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>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM