承上題 [報表程式 - 3] 多個觸發點+多種資料來源 => ViewModel
再假設最後我們發現每個觸發點中,連組裝資料的邏輯都完全各走各的了
所以 BaseEvent 只剩下流程控制與共用類別、參數宣告的功能
除此之外都不能共用了,都會寫在各自的 Event.cs 中
這個例子我會分成四種情境講解
剛好對應到寫報告程式時 最常見的四種型態
這篇是4. 兩事件 (泛型)
完整程式碼請見 https://gitlab.com/jesperlai/Report
故主要的變化為
1. Event 資料夾下針對每個事件各自長出自己的資料夾
2. 資料夾內放每個事件自己的資料觸發點、取資料邏輯、每回合怎麼篩選資料的邏輯
Program.cs
using Sample_4.Event;
using Sample_4.Event.MoveOut;
using Sample_4.Event.Snap;
using Sample_4.Models;
using Sample_4.ViewModels;
using System;
using System.Collections.Generic;
namespace Sample_4
{
class Program
{
private static BuildEventParam MakeBuildEventParam(string eventName)
{
return new BuildEventParam
{
//為了方便講解 (通常這兩個值會從外面傳進來)---------
STime = DateTime.Parse("2001/1/1"),
ETime = DateTime.Parse("2001/2/1"),
//--------------------------------------------------
EventName = eventName,
};
}
static void Main(string[] args)
{
var result = new List<Report>();
//快照
BaseEvent<Lot_Snap> snap = new Event_Snap(MakeBuildEventParam("這是一個快照事件"));
result.AddRange(snap.Run());
//出站
BaseEvent<Lot_Txn> moveOut = new Event_MoveOut(MakeBuildEventParam("這是一個出站事件"));
result.AddRange(moveOut.Run());
//寫出檔案
RptService myRpt = new RptService();
myRpt.GenFile(result, @"D:\sample.csv");
}
}
}
BaseEvent.cs
using Sample_4.Models;
using Sample_4.ViewModels;
using System.Collections.Generic;
namespace Sample_4.Event
{
public abstract class BaseEvent<T>
{
protected BaseDataAccessHelper<T> _helper;
protected Repository repo;
protected BuildEventParam builderParam;
public BaseEvent(BuildEventParam param)
{
builderParam = param;
repo = new Repository();
}
public List<Report> Run()
{
var triggerPoint = GetTriggerPoint();
return GetData(triggerPoint);
}
public abstract List<T> GetTriggerPoint();
public abstract List<Report> GetData(List<T> triggerPoints);
}
}
BaseDataAccessHelper.cs
using Sample_4.ViewModels;
using System.Collections.Generic;
namespace Sample_4.Event
{
public abstract class BaseDataAccessHelper<T>
{
protected Repository _repo;
/// <summary>
/// 通常會傳入一些參數 為了在資料庫裡撈出一些符合的結果來寫出成報表 ex: 撈某一個區間的資料
/// </summary>
public BaseDataAccessHelper(Repository pRepo)
{
_repo = pRepo;
}
public abstract DataSetVM ConcreteDataSet(List<T> triggerPoints);
}
}
MoveOut資料夾內
0_Event_MoveOut.cs
using Sample_4.Extensions;
using Sample_4.Models;
using Sample_4.Utility;
using Sample_4.ViewModels;
using System.Collections.Generic;
namespace Sample_4.Event.MoveOut
{
public class Event_MoveOut : BaseEvent<Lot_Txn>
{
public Event_MoveOut(BuildEventParam param) : base(param)
{
_helper = new MoveOutHelper(repo);
}
public override List<Lot_Txn> GetTriggerPoint()
{
var txns = repo.GetTxn(builderParam.STime, builderParam.ETime, "進站");
return txns;
}
public override List<Report> GetData(List<Lot_Txn> triggerPoints)
{
//完全做自己的事 沒有共用的部份了
var dataSet = _helper.ConcreteDataSet(triggerPoints);
var result = new List<Report>();
//我把Trigger Point 觸發點 縮寫成tp
foreach (var tp in triggerPoints)
{
//該回合需用到的資料 我把this round 縮寫成 tRound
var tRound = MoveOutHelper.GetThisRoundData(dataSet, tp);
//Controller 盡量扁平 如果有複雜的運算可以放到 Utility 做
var item = new Report
{
Lot_No = tp.Lot_No,
Stage_Id = tp.Stage_Id,
Qty = tp.Qty.Trim() - 1, //有些轉型或是常用的運算 寫成extension會比較易用
Po_No = tRound.So.Po_No,
Order_Qty = tRound.So.Qty,
Sod = DataUtility.GetSod(tRound.So), //假設這個欄位有很複雜的邏輯 放到Utitlity做
Event_Name = builderParam.EventName,
};
result.Add(item);
}
return result;
}
}
}
1_MoveOutHelper.DataSet.cs
using Sample_4.Models;
using Sample_4.ViewModels;
using System.Collections.Generic;
using System.Linq;
namespace Sample_4.Event.MoveOut
{
public partial class MoveOutHelper : BaseDataAccessHelper<Lot_Txn>
{
public MoveOutHelper(Repository pRepo) : base(pRepo)
{
}
/// <summary>
/// 其他資料都是based on 你觸發點撈出來的資料有什麼 再去補撈其他資料
/// 例如: 先撈出過站記錄,再透過這段時間內有哪些貨批在動 (表示要報) 再去撈出這些貨批的客戶訂單資料
/// </summary>
public override DataSetVM ConcreteDataSet(List<Lot_Txn> triggerPoints)
{
DataSetVM dataSet = new DataSetVM();
dataSet.So = GetSo(triggerPoints);
return dataSet;
}
private List<Sales_Order> GetSo(List<Lot_Txn> triggerPoints)
{
//通常表示你會有專屬於 MoveOut 的取 So 的條件
var lotNos = triggerPoints.Select(q => q.Lot_No).Distinct().ToList();
var result = _repo.GetSo(lotNos);
return result;
}
}
}
2_MoveOutHelper.ThisRound.cs
using Sample_4.Models;
using Sample_4.ViewModels;
using System.Linq;
namespace Sample_4.Event.MoveOut
{
public partial class MoveOutHelper : BaseDataAccessHelper<Lot_Txn>
{
/// <summary>
/// 因為希望不要一直頻繁開關DB 所以前面一次把所有批號的資料都撈出來了
/// 接下來得篩選出只屬於這回合的資料 (例如 根據這回合的過站記錄的批號 從剛剛的DataSet中找出訂單 => 不是每跑一批就查一次DB )
/// </summary>
public static ThisRoundDataVM GetThisRoundData(DataSetVM src, Lot_Txn tp)
{
ThisRoundDataVM tRound = new ThisRoundDataVM();
tRound.So = GetSo(src, tp);
return tRound;
}
private static Sales_Order GetSo(DataSetVM src, Lot_Txn tp)
{
var Sos = src.So.Where(q => q.Lot_No == tp.Lot_No).ToList();
return Sos.Any() ? Sos.First() : new Sales_Order();
}
}
}
Snap資料夾內
0_Event_Snap.cs
using Sample_4.Extensions;
using Sample_4.Models;
using Sample_4.Utility;
using Sample_4.ViewModels;
using System.Collections.Generic;
namespace Sample_4.Event.Snap
{
public class Event_Snap : BaseEvent<Lot_Snap>
{
public Event_Snap(BuildEventParam param) : base(param)
{
_helper = new SnapHelper(repo);
}
public override List<Lot_Snap> GetTriggerPoint()
{
var snaps = repo.GetSnap();
return snaps;
}
public override List<Report> GetData(List<Lot_Snap> triggerPoints)
{
//完全做自己的事 沒有共用的部份了
var dataSet = _helper.ConcreteDataSet(triggerPoints);
var result = new List<Report>();
//我把Trigger Point 觸發點 縮寫成tp
foreach (var tp in triggerPoints)
{
//該回合需用到的資料 我把this round 縮寫成 tRound
var tRound = SnapHelper.GetThisRoundData(dataSet, tp);
//Controller 盡量扁平 如果有複雜的運算可以放到 Utility 做
var item = new Report
{
Lot_No = tp.Lot_No,
Stage_Id = tp.Stage_Id,
Qty = tp.Qty.Trim() + 1, //有些轉型或是常用的運算 寫成extension會比較易用
Po_No = tRound.So.Po_No,
Order_Qty = tRound.So.Qty,
Sod = DataUtility.GetSod(tRound.So), //假設這個欄位有很複雜的邏輯 放到Utitlity做
Event_Name = builderParam.EventName,
};
result.Add(item);
}
return result;
}
}
}
1_SnapHelper.DataSet.cs
using Sample_4.Models;
using Sample_4.ViewModels;
using System.Collections.Generic;
using System.Linq;
namespace Sample_4.Event.Snap
{
public partial class SnapHelper : BaseDataAccessHelper<Lot_Snap>
{
public SnapHelper(Repository pRepo) : base(pRepo)
{
}
/// <summary>
/// 其他資料都是based on 你觸發點撈出來的資料有什麼 再去補撈其他資料
/// 例如: 先撈出過站記錄,再透過這段時間內有哪些貨批在動 (表示要報) 再去撈出這些貨批的客戶訂單資料
/// </summary>
public override DataSetVM ConcreteDataSet(List<Lot_Snap> triggerPoints)
{
DataSetVM dataSet = new DataSetVM();
dataSet.So = GetSo(triggerPoints);
return dataSet;
}
private List<Sales_Order> GetSo(List<Lot_Snap> triggerPoints)
{
//通常表示你會有專屬於 MoveOut 的取 So 的條件
var lotNos = triggerPoints.Select(q => q.Lot_No).Distinct().ToList();
var result = _repo.GetSo(lotNos);
return result;
}
}
}
2_SnapHelper.ThisRound.cs
using Sample_4.Models;
using Sample_4.ViewModels;
using System.Linq;
namespace Sample_4.Event.Snap
{
public partial class SnapHelper : BaseDataAccessHelper<Lot_Snap>
{
/// <summary>
/// 因為希望不要一直頻繁開關DB 所以前面一次把所有批號的資料都撈出來了
/// 接下來得篩選出只屬於這回合的資料 (例如 根據這回合的過站記錄的批號 從剛剛的DataSet中找出訂單 => 不是每跑一批就查一次DB )
/// </summary>
public static ThisRoundDataVM GetThisRoundData(DataSetVM src, Lot_Snap tp)
{
ThisRoundDataVM tRound = new ThisRoundDataVM();
tRound.So = GetSo(src, tp);
return tRound;
}
private static Sales_Order GetSo(DataSetVM src, Lot_Snap tp)
{
var Sos = src.So.Where(q => q.Lot_No == tp.Lot_No).ToList();
return Sos.Any() ? Sos.First() : new Sales_Order();
}
}
}
其他的程式碼與上一題是一樣的
詳見 [報表程式 - 3] 多個觸發點+多種資料來源 => ViewModel