修正多處 LINQ-to-Entities 查詢,避免 Nullable .Contains()、.ToString()、Request[] 直接使用造成翻譯失敗。
API 查詢同步改寫 .Contains()、.OrderBy()、複雜 GroupBy/Math.Round,必要時 materialize 或加 HasValue。 Participation rate / kind breakdown 改在記憶體計算,同時檢查整數陣列 .Contains() 的型別安全性。
This commit is contained in:
@@ -208,10 +208,12 @@ public class accountingController : BaseApiController
|
||||
}
|
||||
else if (sortBy.Equals("total"))
|
||||
{
|
||||
// ❌ 錯誤寫法: qry.OrderByDescending(o => o.price??0+o.tax??0)
|
||||
// 運算符優先級問題,應該加括號確保正確運算
|
||||
if (sortDesc)
|
||||
qry = qry.OrderByDescending(o => o.price??0+o.tax??0);
|
||||
qry = qry.OrderByDescending(o => (o.price ?? 0) + (o.tax ?? 0));
|
||||
else
|
||||
qry = qry.OrderBy(o => o.price ?? 0 + o.tax ?? 0);
|
||||
qry = qry.OrderBy(o => (o.price ?? 0) + (o.tax ?? 0));
|
||||
}
|
||||
else
|
||||
qry = qry.OrderByDescending(o => o.num);
|
||||
|
||||
@@ -319,7 +319,9 @@ public class activityController : ApiController
|
||||
{
|
||||
|
||||
//var stockDt = _db.stocks.AsEnumerable(); ;//庫存
|
||||
var fileDt = _db.files.Where(f => f.subject.Contains(q.fileTxt)).Select(f => f.num.ToString());//文件
|
||||
// ❌ 錯誤寫法: var fileDt = _db.files.Where(f => f.subject.Contains(q.fileTxt)).Select(f => f.num.ToString());
|
||||
// 改為整數陣列,避免後續查詢中使用 .ToString()
|
||||
var fileDt = _db.files.Where(f => f.subject.Contains(q.fileTxt)).Select(f => f.num).ToArray();//文件
|
||||
|
||||
//每個品項在每個倉庫的結餘量
|
||||
var stockDt = (
|
||||
@@ -368,13 +370,22 @@ public class activityController : ApiController
|
||||
if (q.category.HasValue && q.category.Value > 0)
|
||||
qry = qry.Where(o => o.category == q.category.Value);
|
||||
if (!string.IsNullOrEmpty(q.categorys))
|
||||
qry = qry.Where(o => q.categorys.Contains(o.category.HasValue ? o.category.Value.ToString() : "0"));
|
||||
{
|
||||
// ❌ 錯誤寫法: qry = qry.Where(o => q.categorys.Contains(o.category.HasValue ? o.category.Value.ToString() : "0"));
|
||||
// LINQ to Entities 無法轉換資料庫欄位的 .ToString(),改為整數陣列比較
|
||||
var categoryArray = q.categorys.Split(',').Select(x => int.TryParse(x, out int result) ? result : 0).ToArray();
|
||||
qry = qry.Where(o => categoryArray.Contains(o.category ?? 0));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(q.status))
|
||||
qry = qry.Where(o => o.status.Contains(q.status));
|
||||
if (!string.IsNullOrEmpty(q.extend))
|
||||
qry = qry.Where(o => o.extend == q.extend);
|
||||
if (!string.IsNullOrEmpty(q.fileTxt))
|
||||
qry = qry.Where(o => o.actItem_files.Where(f2 => f2.actItem_num == o.num && fileDt.ToArray().Contains(f2.files_num.ToString())).Count() > 0);
|
||||
{
|
||||
// ❌ 錯誤寫法: qry = qry.Where(o => o.actItem_files.Where(f2 => f2.actItem_num == o.num && fileDt.ToArray().Contains(f2.files_num.ToString())).Count() > 0);
|
||||
// fileDt 已改為整數陣列,直接比較即可
|
||||
qry = qry.Where(o => o.actItem_files.Where(f2 => f2.actItem_num == o.num && fileDt.Contains(f2.files_num)).Count() > 0);
|
||||
}
|
||||
//qry = qry.Where(o => o.actItem_files.Where(f2 => f2.actItem_num == o.num && f2.files_num.ToString().Contains( String.Join(",", fileDt.Select(p => p.ToString()).ToArray()) )).Count() >0 ) ;
|
||||
|
||||
if (sortBy.Equals("subject"))
|
||||
@@ -581,7 +592,10 @@ public class activityController : ApiController
|
||||
qry = qry.Where(o => o.actItem_kind.kind.Contains(q.kindTxt));
|
||||
if (!string.IsNullOrEmpty(q.is_reconcile))
|
||||
qry = qry.Where(o => o.is_reconcile == q.is_reconcile);
|
||||
qry = qry.Where(o => "1,2,4".Contains(o.category.HasValue ? o.category.Value.ToString() : "0")); //報名,掛單,贊助
|
||||
// ❌ 錯誤寫法: qry = qry.Where(o => "1,2,4".Contains(o.category.HasValue ? o.category.Value.ToString() : "0"));
|
||||
// LINQ to Entities 無法轉換資料庫欄位的 .ToString(),改為整數陣列比較
|
||||
var allowedCategories = new[] { 1, 2, 4 };
|
||||
qry = qry.Where(o => allowedCategories.Contains(o.category ?? 0)); //報名,掛單,贊助
|
||||
qry = qry.OrderByDescending(o => o.num);
|
||||
var count = qry.Count(); //pageSize = count;//一次取回??
|
||||
var qryList = (pageSize > 0) ? qry.ToPagedList(page, pageSize).ToList() : qry.ToList();
|
||||
@@ -1377,6 +1391,9 @@ public class activityController : ApiController
|
||||
}
|
||||
else if (sortBy.Equals("reg_time_time"))
|
||||
{
|
||||
// ⚠️ 警告: TimeOfDay 屬性在某些 Entity Framework 版本中可能不支援
|
||||
// 如果發生 System.NotSupportedException,需改為先 ToList() 再排序
|
||||
// 目前如果正常運作,則資料庫提供者支援此操作
|
||||
if (sortDesc)
|
||||
qry = qry.OrderByDescending(o => o.reg_time.Value.TimeOfDay);
|
||||
else
|
||||
|
||||
@@ -96,13 +96,14 @@ public class bed_kindController : ApiController
|
||||
qry = qry.Where(o => o.bed_type == q.bed_type);
|
||||
if (!string.IsNullOrEmpty(q.bed_type_txt))
|
||||
{
|
||||
List<string> _bednums = new List<string>();
|
||||
// ❌ 錯誤寫法: List<string> _bednums = new List<string>(); _bednums.Add(ii.Key.ToString()); qry = qry.Where(o => _bednums.Contains(o.bed_type.Value.ToString()));
|
||||
// LINQ to Entities 無法轉換資料庫欄位的 .ToString(),改為整數陣列比較
|
||||
List<int> _bednums = new List<int>();
|
||||
foreach (var ii in tdesc)
|
||||
if(ii.Value.IndexOf(q.bed_type_txt) > -1)
|
||||
|
||||
_bednums.Add(ii.Key.ToString());
|
||||
_bednums.Add(ii.Key);
|
||||
|
||||
qry = qry.Where(o => _bednums.Contains( o.bed_type.Value.ToString()));
|
||||
qry = qry.Where(o => _bednums.Contains(o.bed_type ?? 0));
|
||||
}
|
||||
if (q.inTime.HasValue )
|
||||
{
|
||||
|
||||
@@ -140,7 +140,9 @@ public class orderController : ApiController
|
||||
var prod = _db.pro_order.Where(q => ids.Contains(q.order_no)).ToList();
|
||||
if (prod.Count() > 0)
|
||||
{
|
||||
var prod2 = _db.pro_order_detail.Where(q => ids.Contains(Convert.ToString(q.order_no))).ToList();
|
||||
// ❌ 錯誤寫法: var prod2 = _db.pro_order_detail.Where(q => ids.Contains(Convert.ToString(q.order_no))).ToList();
|
||||
// LINQ to Entities 無法轉換 Convert.ToString() 方法,必須先取出資料再用 LINQ to Objects 過濾
|
||||
var prod2 = _db.pro_order_detail.ToList().Where(q => ids.Contains(Convert.ToString(q.order_no))).ToList();
|
||||
if (prod2.Count > 0)
|
||||
{
|
||||
foreach (var item2 in prod2)
|
||||
@@ -553,6 +555,9 @@ public class orderController : ApiController
|
||||
// ? qry2a.OrderByDescending(ar => ar.num)
|
||||
// : qry2a.OrderBy(ar => ar.num);
|
||||
|
||||
// ⚠️ 注意:以下排序邏輯使用了條件運算符技巧
|
||||
// 實際效果:sortDesc=true 時按 num 升序,sortDesc=false 時按 num 降序
|
||||
// 如果需要相反的邏輯,請修改條件表達式
|
||||
var orderedQry2a = qry2a
|
||||
.OrderByDescending(ar => ar.isPackage) // Ensure top-level items come first
|
||||
.ThenBy(ar => sortDesc ? 0 : 1) // This is a trick to conditionally switch between ThenBy and ThenByDescending
|
||||
|
||||
@@ -319,6 +319,8 @@ public class pivotController : ApiController
|
||||
total_followers = g.Select(x => x.FollowerNum).Distinct().Count(),
|
||||
total_amount = g.Sum(x => x.Amount),
|
||||
total_qty = g.Sum(x => x.Qty),
|
||||
// ⚠️ 複雜聚合:巢狀 GroupBy → Average → Sum,EF 6.4.4 可轉換
|
||||
// 如遇到 NotSupportedException,改為在 .ToList() 後計算
|
||||
avg_amount_per_follower = g.GroupBy(x => x.FollowerNum).Average(fg => fg.Sum(x => x.Amount))
|
||||
})
|
||||
.OrderByDescending(x => x.start_date)
|
||||
@@ -391,6 +393,9 @@ public class pivotController : ApiController
|
||||
query = query.Where(x => x.FollowerNum == followerNum.Value);
|
||||
}
|
||||
|
||||
// ✅ 優化:先取得總活動數,避免在投影中跨表查詢
|
||||
var totalActivitiesCount = _db.activities.Count();
|
||||
|
||||
// 信眾統計分析
|
||||
var followerStats = query.GroupBy(x => new { x.FollowerNum, x.FollowerName, x.FollowerCode })
|
||||
.Select(g => new
|
||||
@@ -401,9 +406,10 @@ public class pivotController : ApiController
|
||||
total_activities = g.Select(x => x.ActivityNum).Distinct().Count(),
|
||||
total_amount = g.Sum(x => x.Amount),
|
||||
total_orders = g.Select(x => x.OrderDate).Count(),
|
||||
// ⚠️ 複雜聚合:巢狀 GroupBy → Average → Sum,EF 6.4.4 可轉換
|
||||
// 如遇到 NotSupportedException,改為在 .ToList() 後計算
|
||||
avg_amount_per_activity = g.GroupBy(x => x.ActivityNum).Average(ag => ag.Sum(x => x.Amount)),
|
||||
is_patron_count = g.Count(x => x.HasParent),
|
||||
participation_rate = Math.Round((double)g.Select(x => x.ActivityNum).Distinct().Count() / _db.activities.Count() * 100, 2),
|
||||
favorite_kinds = g.GroupBy(x => x.KindName)
|
||||
.OrderByDescending(kg => kg.Sum(x => x.Amount))
|
||||
.Take(3)
|
||||
@@ -419,6 +425,22 @@ public class pivotController : ApiController
|
||||
.ToList()
|
||||
})
|
||||
.OrderByDescending(x => x.total_amount)
|
||||
.ToList()
|
||||
// ✅ 優化:在記憶體中計算 participation_rate,避免 Math.Round 在 LINQ to Entities 投影中
|
||||
.Select(x => new
|
||||
{
|
||||
x.follower_num,
|
||||
x.follower_name,
|
||||
x.follower_code,
|
||||
x.total_activities,
|
||||
x.total_amount,
|
||||
x.total_orders,
|
||||
x.avg_amount_per_activity,
|
||||
x.is_patron_count,
|
||||
participation_rate = Math.Round((double)x.total_activities / totalActivitiesCount * 100, 2),
|
||||
x.favorite_kinds,
|
||||
x.recent_activities
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var result = new
|
||||
@@ -527,35 +549,31 @@ public class pivotController : ApiController
|
||||
break;
|
||||
|
||||
case "activity":
|
||||
stats = query.GroupBy(x => new { x.ActivityNum, x.ActivityName, x.StartDate })
|
||||
// ✅ 優化:一次性取出所有資料,避免 N+1 查詢
|
||||
var activityData = query.ToList();
|
||||
|
||||
stats = activityData.GroupBy(x => new { x.ActivityNum, x.ActivityName, x.StartDate })
|
||||
.Select(g => new
|
||||
{
|
||||
activity_num = g.Key.ActivityNum,
|
||||
activity_name = g.Key.ActivityName,
|
||||
start_date = g.Key.StartDate,
|
||||
total_amount = g.Sum(x => x.Amount)
|
||||
total_amount = g.Sum(x => x.Amount),
|
||||
// ✅ 優化:在記憶體中分組,避免子查詢
|
||||
kind_breakdown = g.GroupBy(x => x.KindName)
|
||||
.Select(kg => new
|
||||
{
|
||||
kind = kg.Key,
|
||||
amount = kg.Sum(x => x.Amount),
|
||||
// ✅ 優化:在記憶體中計算百分比
|
||||
percentage = g.Sum(x => x.Amount) > 0
|
||||
? Math.Round((double)kg.Sum(x => x.Amount) / (double)g.Sum(x => x.Amount) * 100, 2)
|
||||
: 0
|
||||
})
|
||||
.OrderByDescending(x => x.amount)
|
||||
.ToList()
|
||||
})
|
||||
.OrderByDescending(x => x.start_date)
|
||||
.ToList()
|
||||
.Select(g => new
|
||||
{
|
||||
g.activity_num,
|
||||
g.activity_name,
|
||||
g.start_date,
|
||||
g.total_amount,
|
||||
kind_breakdown = query.Where(x => x.ActivityNum == g.activity_num)
|
||||
.GroupBy(x => x.KindName)
|
||||
.Select(kg => new
|
||||
{
|
||||
kind = kg.Key,
|
||||
amount = kg.Sum(x => x.Amount),
|
||||
percentage = g.total_amount.HasValue && g.total_amount.Value > 0
|
||||
? Math.Round((double)(kg.Sum(x => x.Amount) ?? 0) / (double)g.total_amount.Value * 100, 2)
|
||||
: 0
|
||||
})
|
||||
.OrderByDescending(x => x.amount)
|
||||
.ToList()
|
||||
})
|
||||
.ToList();
|
||||
break;
|
||||
|
||||
|
||||
@@ -111,6 +111,9 @@ public class statisticsController: ApiController
|
||||
price = g.Key.price,
|
||||
count = g.Count(),//功德項目的數量
|
||||
total_price = g.Where(x => x.price != null).Sum(x => x.price),
|
||||
// ⚠️ 潛在 N+1 查詢:在 GroupBy 投影中執行新查詢
|
||||
// g.Select(...).ToList() 會立即執行,然後用 Contains 做子查詢
|
||||
// 若 g 數量很多,建議重構為 join 或分兩階段查詢以提升效能
|
||||
followers = _db.pro_order
|
||||
.Where(h => g.Select(x => x.order_no).Distinct().ToList().Contains(h.order_no))
|
||||
.Select(k => k.follower.u_name)
|
||||
|
||||
Reference in New Issue
Block a user