using System.Collections; using System.Collections.Specialized; using System.Net.Http.Formatting; using System.Reflection; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; namespace YLErp { [TestClass] public class UnitTestBase { protected OptUserInfo GetOptUser([CallerMemberName] string m_name = "") { if (string.IsNullOrEmpty(m_name) || m_name == ".ctor") { m_name = GetType().Name; } return new OptUserInfo(0, m_name, OptUserFrom.UnitTest, userGroup: null); } /// /// 读取资源文件所有文本 /// protected static string ReadResourceFile(string relativeFilePath) { var path = Path.Combine(AppContext.BaseDirectory, "Resources\\" + relativeFilePath); return File.ReadAllText(path); } /// /// 读取资源文件所有行 /// protected static string[] ReadResourceFileLines(string relativeFilePath) { var path = Path.Combine(AppContext.BaseDirectory, "Resources\\" + relativeFilePath); return File.ReadAllLines(path); } protected static T DeserializeFormData(string resourceFileRelativePath) where T : new() { var list = new List>(); var lines = ReadResourceFileLines(resourceFileRelativePath); foreach (var line in lines) { if (string.IsNullOrWhiteSpace(line)) { continue; } var index = line.IndexOf(':'); if (index < 0) { continue; } list.Add(new KeyValuePair(line.Substring(0, index).Trim(), line.Substring(index + 1).Trim())); } var fc = new FormDataCollection(list); return MapForm(fc.ReadAsNameValueCollection(), ""); } protected class OtcOptionTradeFullFormData { public IEnumerable trades { get; set; } } private static readonly Regex _regex = new(@"\[(?.*?)\]", RegexOptions.Compiled | RegexOptions.Singleline); private static T MapForm(NameValueCollection nvc, string rootObjectName) where T : new() { var result = new T(); foreach (var kvp in nvc.AllKeys) { if (!kvp.StartsWith(rootObjectName)) { throw new Exception("All keys should start with " + rootObjectName); } var match = _regex.Match(kvp.Remove(0, rootObjectName.Length)); if (match.Success) { // build path in a form of [Documents, 0, DocumentID]-like array var path = new List(); while (match.Success) { path.Add(match.Groups["value"].Value); match = match.NextMatch(); } // this is object we currently working on object currentObject = result; for (var i = 0; i < path.Count; i++) { var last = i == path.Count - 1; var propName = path[i]; if (int.TryParse(propName, out var index)) { // index access, like [0] var list = currentObject as IList; if (list == null) { throw new Exception("Invalid index access expression"); // more info here } // get the type of item in that list (i.e. Document) var args = list.GetType().GetGenericArguments(); var listItemType = args[0]; if (last) { // may need more sophisticated conversion from string to target type list[index] = Convert.ChangeType(nvc[kvp], Nullable.GetUnderlyingType(listItemType) ?? listItemType); } else { // if not initialized - initalize var next = index < list.Count ? list[index] : null; if (next == null) { // handle IList case in a special way here, since you cannot create instance of interface next = Activator.CreateInstance(listItemType); // fill with nulls if not enough items yet while (index >= list.Count) { list.Add(null); } list[index] = next; } currentObject = next; } } else { var prop = currentObject.GetType().GetProperty(propName, BindingFlags.Instance | BindingFlags.Public); if (last) { // may need more sophisticated conversion from string to target type prop.SetValue(currentObject, Convert.ChangeType(nvc[kvp], Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType)); } else { // if not initialized - initalize var next = prop.GetValue(currentObject); if (next == null) { // TODO: handle IList case in a special way here, since you cannot create instance of interface next = Activator.CreateInstance(prop.PropertyType); prop.SetValue(currentObject, next); } currentObject = next; } } } } } return result; } } }