前情提要
前提情要請參考[DotnetCore]泛型運用系列-Model設計篇,不過筆者還是在這邊再列一下會用到的技術觀念與套件:
- AutoMapper:運用於將檔案內容對應的物件轉成
EFCore
的物件時
- Generic Class/Method: 不想寫多個
Service
去處理多個檔案,設計成泛型形式以符合各種檔案類型
Attribute
: 透過Description Attribute
,註記其對應的Comlumn
欄位順序
- Coravel:透過該套件,將寫好的
Service
掛成排程任務
- Extension Method:有一些通用的
Method
,不要落落等塞在一個Service中,因此轉換成可以共用的Extension Method
- EFCore:資料存取用開發套件
內容
這篇主要是存取Attribute
的方式設定出泛型在指派值的邏輯,以筆者這套實作邏輯中會用到Column
順序,這就因人而異,剛好筆者要解析的資料內容,會將每個Column
的值使用一個特殊符號隔開,因此筆者設計一個Column
順序的值設定於Attribute
中,使泛型Method
中取得Type
的GetProperties
方法並取得對應的CustomAttribute
的設定內容,因而可以寫出共用的實體屬性值指派的商業邏輯。
以上述描述的情境來說,筆者不想要再宣告其他自定義的Attribute
,因此偷懶使用既有的Description Attribute
當作Column
順序的設定值,示意如下
1 2 3 4 5 6 7 8 9 10
| [Description("A.txt")] public class ADomainModel : BaseModel { [Description("1")] public string AProperty1 { get; set; } [Description("2")] public string AProperty2 { get; set; } [Description("3")] public string AProperty3 { get; set; } }
|
筆者將Description Attribute
用得淋漓盡致,Class
層級的Description
中設定其對應的txt檔案名稱
,Property
層級的Description
則設定其Column
順序值。接著不想要寫落落等取得Description Attribute
中的設定值,寫一個Extension Method
,名為CustomTypeExtension
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public static class CustomTypeExtension { public static string GetDescription(Type type) { var descriptions = (DescriptionAttribute[]) type.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (descriptions.Length == 0) { return null; } return descriptions[0].Description; }
public static Dictionary<int, string> GetPropertiesWithOrderDescription(Type type) { Dictionary<int, string> propertyDic = new Dictionary<int, string>(); foreach (var property in type.GetProperties()) { var order = property.GetCustomAttribute<DescriptionAttribute>()?.Description; if (string.IsNullOrEmpty(order) == false) propertyDic.Add(int.Parse(order), property.Name); } return propertyDic; } }
|
接著寫一個共用的Extension Method
,將透過Activator.CreateInstance<T>()
初始化的實體,針對實體中的屬性指派值的商業邏輯抽離至Extension Method
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public static class ObjectExtension { public static List<TResult> GetModelList<TResult>(string localFilePath , Encoding encoding , Dictionary<int, string> orderDict , char splictChar) { var modelList = new List<TResult>(); using var sr = new StreamReader(localFilePath, encoding); while (sr.Peek() >= 0) { var line = sr.ReadLine(); if (string.IsNullOrEmpty(line)) continue; var infoList = line.Split(splictChar); var model = Activator.CreateInstance<TResult>(); for (int i = 0; i < infoList.Length; i++) { var propertyInfo = typeof(TResult).GetProperties() .First(x => x.Name == orderDict.First(x => x.Key == (i + 1)).Value); propertyInfo.SetValue(model, infoList[i].Trim()); } modelList.Add(model); } return modelList; } }
|
跟著筆者來看看怎麼運用吧
1 2 3 4 5 6 7 8 9 10 11 12
|
var fileName = CustomTypeExtension.GetDescription(_model.GetType()); var orderDict = CustomTypeExtension.GetPropertiesWithOrderDescription(_model.GetType());
var filePath = Path.Combine(_env.ContentRootPath, "upload", "Downloads", fileName); var modelList = ObjectExtension.GetModelList<T>(filePath, Encoding.GetEncoding("Big5") , orderDict, '=');
|
結論
透過此篇的解構,筆者相信真正的Domain Service
會變得很乾淨,且此篇撰寫出來的Extension Method可以給其他服務使用,當然筆者最終的目標是將下載檔案,將檔案中的資料內容存進資料庫這樣的行為變成是一個透過設定就能使用的服務,離目標不遠了,下篇見啦。