1. 程式人生 > 程式設計 >如何在Asp.Net Core中整合ABP Dapper

如何在Asp.Net Core中整合ABP Dapper

  在實際的專案中,除了整合ABP框架的EntityFrameworkCore以外,在有些特定的場景下不可避免地會使用一些SQL查詢語句,一方面是由於現在的EntityFrameworkCore2.X有些問題沒有解決,另外一方面是基於效能方面的考慮,在瞭解本篇內容之前,首先還是來看看官方文件來給出的說明。

  按照官方的介紹整體可以分為下面的步驟:1 安裝依賴包。2 新增DependsOn屬性標籤。3 Entity to Table Mapping。 4 Usage 通過上面的4個步驟我們就能夠正常在Asp.Net Core專案中使用ABP Dapper了,下面我們就具體的過程來做進一步的說明。

  一 安裝包依賴

  這個不做過多的解釋,通過Nuget 包管理器或者通過程式包管理控制檯來新增Abp.Dapper的引用,在我們實際的專案中整個類庫的結構如下圖所示,包含Dapper和EntityFrameworkCore兩種方案。

如何在Asp.Net Core中整合ABP Dapper

  二 新增DependsOn屬性標籤

  後面我們就需要在我們當前類庫專案中唯一的SalesDataModule中來做一些初始化和新增DependsOn標籤的操作了。

[DependsOn(typeof(AbpZeroCoreEntityFrameworkCoreModule))]
[DependsOn(typeof(AbpDapperModule))]
public class SalesDataModule : AbpModule {
public override void Initialize() {
IocManager.RegisterAssemblyByConvention(typeof(SalesDataModule).GetAssembly());
DapperExtensions.DapperExtensions.SetMappingAssemblies(new List<Assembly> { typeof(SalesDataModule).GetAssembly() });
}
}

  這裡我們應該瞭解為什麼要新增依賴關係?這個我們當前的SalesDataModule會依賴於AbpDapperModule和AbpZeroCoreEntityFrameworkCoreModule,確立了這樣的依賴關係後,在ABP框架中就會將當前Module所依賴的其它Module放到List<AbpModule>的前面,這樣通過這樣對的層層依賴關係進行拓撲排序就能夠保證被依賴的AbpModule一定先進行初始化操作,這樣就能夠避免引用關係的錯誤,從而導致程式碼邏輯的錯誤,具體說來:如果A 依賴於B,B依賴於C,那麼這三個模組之間的排序為C B A,這樣在整個Module系統初始化的時候,會先執行Module C的PreIntialize()、Initialize()、PostInitialize()方法,我們看看ABP中的原始碼。

public virtual void StartModules()
{
var sortedModules = _modules.GetSortedModuleListByDependency();
sortedModules.ForEach(module => module.Instance.PreInitialize());
sortedModules.ForEach(module => module.Instance.Initialize());
sortedModules.ForEach(module => module.Instance.PostInitialize());
}

  這個裡面sortedModules就是通過這種依賴關係進行拓撲排序的,然後依次這行每個模組中的這幾個方法進行一些初始化的操作。

  三 Entity to Table Mapping

  這個按照官方的解釋就是在建立Entity和資料庫實體之間的關係,這個類似於在Domain層實體上面新增【Table】標籤(個人理解),在這個裡面我們還能新增一些其它特性,比如Ignore掉一些導航屬性等等......

public sealed class VehicleOrderPlanMapper : ClassMapper<VehicleOrderPlan> {
public VehicleOrderPlanMapper() {
Table("VehicleOrderPlan");
Map(x => x.Branch).Ignore();
AutoMap();
}
}

  四 應用

  這個部分就結合具體的專案來談一談一些細節方面的東西,我們先來看看具體的程式碼。

public class VehicleOrderPlanDapperRepository : DcsDapperRepositoryBase<VehicleOrderPlan>,IVehicleOrderPlanDapperRepository {
public VehicleOrderPlanDapperRepository(IActiveTransactionProvider activeTransactionProvider) : base(activeTransactionProvider) {
}

public IEnumerable<WeeklyOrderPlanSummary> GetWeeklyOrderPlanSummary(int? yearOfPlan,int? weekOfPlan,string provinceName,[CanBeNull]VehicleOrderPlanType[] planType,string marketName,PageRequest page) {
var sqlParam = new StringBuilder()
.AppendIf(yearOfPlan.HasVa程式設計客棧lue,$" AND p.YearOfPlan = :{nameof(yearOfPlan)}")
.AppendIf(weekOfPlan.HasValue,$" AND p.WeekOfPlan = :{nameof(weekOfPlan)}")
.AppendIf(!provinceName.IsNullOrWhiteSpace(),$@" AND EXISTS ( SELECT 1
FROM Company C WHERE c.Id = p.DealerId AND c.Status <> {(int)MasterDataStatus.作廢}
AND c.ProvinceName like '%' || :{nameof(provinceName)} || '%')");

var planTypes = new[] {
VehicleOrderPlanType.周度計劃,VehicleOrderPlanType.小品種計劃,VehicleOrderPlanType.移庫計劃
};
if (planType != null && planType.Length > 0)
planTypes = planTypes.Intersect(planType).ToArray();

var departmentParam = string.Empty;
if (!marketName.IsNullOrWhiteSpace())
departmentParam = $" AND (m.Name LIKE '%' || :{nameof(marketName)} || '%')";

var sql = $@"
SELECT p.YearOfPlan,p.WeekOfPlan,TRUNC(p.CreateTime) AS CreateTime,TRUNC(p.StartTime) AS StartTime,TRUNC(p.EndTime) AS EndTime,pd.ProductCode,pd.ProductName,pd.ProductType,pd.ProductCategoryCode AS VehicleModelCode,pd.ProductCategoryName AS VehicleModelName,Sum(pd.PlannedQuantity) AS PlannedQuantity,Sum(pd.FirstPlannedQuantity) AS FirstPlannedQuantity,Sum(pd.QuantityOfAssessment) AS QuantityOfAssessment,Sum(pd.ConfirmedQuantity) AS ConfirmedQuantity
FROM VehicleOrderPlan p
CROSS JOIN VehicleOrderPlanDetail pd
WHERE (p.Status <> {(int)VehicleOrderPlanStatus.作廢} AND p.Type in {planTypes.ToSqlInParam()} {sqlParam} AND EXISTS (
SELECT 1
FROM DealerMarketDptRelation dm
WHERE (((dm.BranchId = p.BranchId) AND (dm.DealerId = p.DealerId)) AND (dm.Status = {(int)BaseDataStatus.有效})) AND EXISTS (
SELECT 1
FROM MarketingDepartment m
WHERE ((m.BranchCode = {SunlightConsts.DEFAULT_BRANCH_QRSALESLTD}) AND (m.Status = {(int)BaseDataStatus.有效})) {departmentParam}
AND (m.Id = dm.MarketId)))) AND (p.Id = pd.VehicleOrderPlanId)
GROUP BY p.YearOfPlan,TRUNC(p.CreateTime),TRUNC(p.StartTime),TRUNC(p.EndTime),pd.ProductCategoryCode,pd.ProductCategoryName";

return QueryPaged<WeeklyOrderPlanSummary>(sql,page,new {
yearOfPlan,weekOfPlan,provinceName,marketName
});
}
}

  這段程式碼主要是通過具體傳入的引數計劃年、計劃周、省份......等引數到資料庫中查詢相關的記錄,這裡我們先看看基類DcsDapperRepositoryBase<VehicleOrderPlan>裡面做了些什麼?

public class DcsDapperRepositoryBase<TEntity> : DapperEfRepositoryBase<DcsDbContext,TEntity>
where TEntity : class,IEntity<int> {
public DcsDapperRepositoryBase(IActiveTransactionProvider activeTransactionProvider) : base(activeTransactionProvider) {
}

/// <summary>
/// 以分頁的形式查詢資料
/// </summary>
/// <typeparam name="TValueObject"></typeparam>
/// <param name="sql"></param>
/// <param name="pageRequest"></param>
/// <param name="parameters">引數的匿名物件</param>
/// <returns></returns>
protected IEnumerable<TValueObject> QueryPaged<TValueObject>(string sql,PageRequest pageRequest,object parameters = null)
where TValueObject : ValueObjectBase {
var orderCondition = (string.IsNullOrWhiteSpace(pageRequest.Ordering) ? string.Empty : "ORDER BY " + pageRequest.Ordering);
orderCondition.SqlInjectionInspect();
var pagedSql = $@"WITH ""_data"" AS ({sql}),""_count"" AS (SELECT COUNT(0) AS OverallCount FROM ""_data"")
SELECT *
FROM (SELECT A.*,ROWNUM AS ""RowNum""
FROM (SELECT * FROM ""_data""
&nbstEIOyraGmp;{orderCondition}) A
WHERE ROWNUM <= {pageRequest.PageSize * (pageRequest.PageIndex + 1)}) B,""_count""
WHERE ""RowNum"" > {pageRequest.PageSize * pageRequest.PageIndex}";
return Query<TValueObject>(pagedSql,parameters);
&ntEIOyraGmbsp;}
}

  在這個基類中我們繼承了ABP中的基類DapperEfRepositoryBase<DcsDbContext,TEntity>,這個泛型基類第一個引數就是我們專案中的具體DbContext,第二個引數就是我們具體定義的實體,這個實體是主鍵為Int的自增長型別。這裡面由於查詢的資料非常多,所以我們這裡實際上返回的是分頁的第一頁的結果集,這裡還有一個重要的知識就是,為了防止sql注入,這裡sql中的引數都採用引數的匿名物件,而不是直接進行拼接,這個是防止SQL注入的時候最常見的方式。通過這個具體的例子你應該知道怎樣在ABP Dapper中使用匿名引數物件來防止SQL注入,另外通過這段SQL你還知道在Oracle資料庫中如何對查詢到的結果進行分頁處理。

  在處理完這些後,我們再來看看當前VehicleOrderPlanRepositortEIOyraGmy繼承的介面是在哪裡進行定義的?具體的領域層又該如何進行呼叫?

public interface IVehicleOrderPlanDapperRepository : IDapperRepository<VehicleOrderPlan> {
IEnumerable<WeeklyOrderPlanSummary> GetWeeklyOrderPlanSum程式設計客棧mary(int? yearOfPlan,[CanBeNull] VehicleOrderPlanType[] planType,PageRequest page);
}

  這個介面非常簡單,但是這個介面究竟應該在哪裡進行定義呢?我們按照DDD思想,首先想到的就是在領域層進行定義,不然領域層其它業務該在哪裡呼叫這個方法呢?那麼這個可以從哪裡找到答案呢?

如何在Asp.Net Core中整合ABP Dapper

  有沒有對這張圖很熟悉,這個就是用於介紹ABP N層架構的示意圖,紅框標註的就是具體的結構中的介面定義和實現,這兩者的定義和實現分別是處於不同的層中,一個屬於Domain Layer中而具體的實現位於Infrastructure Layer中,一層定義介面,而另外一層則定義具體的實現,有了這個你是不是對整個ABP的架構有了更深入的理解呢?

以上就是如何在Asp.Net Core中整合ABP Dapper的詳細內容,更多關於Asp.Net Core中整合ABP Dapper的資料請關注我們其它相關文章!