一般情況下,使用EF中的查詢語法和方法語法可以幫助我們完成絕大部分業務,但是也有特殊的情況需要直接執行Sql語句。比如,我們的業務過於復雜繁瑣,或是有些業務使用EF操作時比較復雜,但是使用Sql時會很簡單等等。
今天做項目的時候,就由於使用EF來處理邏輯時格外棘手,最終選擇了直接執行Sql語句,下面說說具體的業務。
一共關系到三張表,分別是Operators(運營商表),Orders(訂單表)和DeviceCoinsRecords(設備投幣記錄表),相應表的定義已經截圖如下,就不在這里編寫代碼了。Operators里放運營商的數據信息,Orders是手機端掃碼下單時產生的訂單數據,DeviceCoinsRecords是向機器設備投幣時產生的數據。
用下面的一張圖解釋一下:
下面是三張表的結構:
三張表之間的關聯關系是:Orders和和DeviceCoinsRecords通過OperatorId關聯Operators表的Id主鍵。
要實現的業務是:
Sql語句表達:
SELECT ROW_NUMBER() OVER(ORDER BY Id ) AS RowId,Id,Name,AllocateRatio,
(SELECT SUM(PayFee) FROM dbo.Orders WHERE Status NOT IN(0,3,4) AND OperatorID=dbo.Operators.Id AND YEAR(OrderDate)=@Year1 AND MONTH(OrderDate)=@Month1) AS TotalScanCode,
(SELECT SUM(TheDayMoney) FROM dbo.DeviceCoinsRecords WHERE dbo.DeviceCoinsRecords.OperatorID=dbo.Operators.Id AND Year=@Year2 AND Month=@Month2) AS TotalCoinsCast
FROM dbo.Operators
視圖表達:
自然語言表達:
匯總運營商的年度和月度報表,也可選擇具體的運營商和年月進行匯總。
一開始我是拒絕在EF中使用SQL查詢的,但是花了好長時間,實在是找不到可以替代以上sql的linq語句或者Lambda方法語法,有哪位能寫出來的,歡迎挑戰一下,但是寫出來的很復雜的話,也沒這個必要了。
下面說說具體的實現【首先要聲明的是我使用的是ABP框架】。
1.在核心層(Core層)的IRepositories文件下創建一個接口IOperatorReportRepository
2.該接口的定義如下代碼所示:
public interface IOperatorReportRepository:IRepository<Operators>
{
Task<IEnumerable<OperatorYearOrMonthReport>> QueryMonthReports(int year, int month, int operatorId);
Task<IEnumerable<OperatorYearOrMonthReport>> QueryYearReports(int year, int operatorId);
}
3.實現接口代碼:
public class OperatorReportReposiroty:ChargeStationRepositoryBase<Operators>,IOperatorReportRepository
{
public OperatorReportReposiroty(IDbContextProvider<ChargeStationDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async Task<IEnumerable<OperatorYearOrMonthReport>> QueryMonthReports(int year, int month,int operatorId=0)
{
string whereClause = string.Empty;
if (operatorId>0)
{
whereClause += string.Format(" where Id={0} ",operatorId);
}
string sql = string.Format(@"
SELECT ROW_NUMBER() OVER(ORDER BY Id ) AS RowId,Id,Name,AllocateRatio,
(SELECT SUM(PayFee) FROM dbo.Orders WHERE Status NOT IN(0,3,4) AND OperatorID=dbo.Operators.Id AND YEAR(OrderDate)=@Year1 AND MONTH(OrderDate)=@Month1) AS TotalScanCode,
(SELECT SUM(TheDayMoney) FROM dbo.DeviceCoinsRecords WHERE dbo.DeviceCoinsRecords.OperatorID=dbo.Operators.Id AND Year=@Year2 AND Month=@Month2) AS TotalCoinsCast
FROM dbo.Operators {0}", whereClause);
return await Context.Database.SqlQuery<OperatorYearOrMonthReport>(sql,
new SqlParameter("@Year1", year),
new SqlParameter("@Month1", month),
new SqlParameter("@Year2", year),
new SqlParameter("@Month2", month)).ToListAsync();
}
public async Task<IEnumerable<OperatorYearOrMonthReport>> QueryYearReports(int year, int operatorId = 0)
{
string whereClause = string.Empty;
if (operatorId>0)
{
whereClause += string.Format(" where Id={0} ", operatorId);
}
string sql = string.Format(@"
SELECT ROW_NUMBER() OVER(ORDER BY Id )AS RowId,Id,Name,AllocateRatio,
(SELECT SUM(PayFee) FROM dbo.Orders WHERE Status NOT IN(0,3,4) AND OperatorID=dbo.Operators.Id AND YEAR(OrderDate)=@Year3) AS TotalScanCode,
(SELECT SUM(TheDayMoney) FROM dbo.DeviceCoinsRecords WHERE dbo.DeviceCoinsRecords.OperatorID=dbo.Operators.Id AND Year=@Year4) AS TotalCoinsCast
FROM dbo.Operators {0}", whereClause);
return await Context.Database.SqlQuery<OperatorYearOrMonthReport>(sql,
new SqlParameter("@Year3", year),
new SqlParameter("@Year4", year)).ToListAsync();
}
}
這里有點意思的是,如果SqlQuery的后面沒有ToList()或者ToListAsync(),那么你會收獲一個奇妙的禮物!你可以試試看!
4.在應用服務層調用
public async Task<PagedResultOutput<GetOperatorReportsOutput>> GetOperatorReports(GetOperatorReportsInput input)
{
int operatorId = string.IsNullOrEmpty(input.Code)
? 0 : _operatorRepository.GetAll().Single(o => o.Code == input.Code).Id;
IEnumerable<OperatorYearOrMonthReport> operatorMonthReports;
if (input.Year.HasValue && input.Month.HasValue)
{
operatorMonthReports =await _operatorReportRepository.QueryMonthReports(input.Year.Value,input.Month.Value,operatorId);
}else if (input.Year.HasValue)
{
operatorMonthReports =await _operatorReportRepository.QueryYearReports(input.Year.Value,operatorId);
}
else
{
if (input.IsThisYear)
{
operatorMonthReports = await _operatorReportRepository.QueryYearReports(DateTime.Now.Year,operatorId);
}
else
{
operatorMonthReports =await _operatorReportRepository.QueryMonthReports(DateTime.Now.Year,DateTime.Now.Month,operatorId);
}
}
var totalCount = operatorMonthReports.Count();
var operatorReportOutputList = operatorMonthReports.MapTo<List<GetOperatorReportsOutput>>();
return new PagedResultOutput<GetOperatorReportsOutput>(totalCount, operatorReportOutputList);
}
好了,這次主要記錄一下在EF中如何直接調用Sql語句的用法,以后有機會再說說關於在EF中存儲過程(包括輸出參數的情況)的調用。