/// <summary> /// Build ORMTableInfo class instance from dynamic query result with default provider binding /// </summary> /// <param name="query_result">IEnumerable<dynamic>dapper query result</param> /// <param name="table_name">Table name</param> /// <param name="As">as short name</param> /// <returns>ORMTableInfo</returns> public static ORMTableInfo ORMFromDynamicQueryResult(IEnumerable <dynamic> query_result, string table_name, string As = null) { var columnset = new List <column_set_item>(); var first_round = true; foreach (var row in query_result) { if (first_round) { first_round = false; foreach (var pair in row) { columnset.Add(new column_set_item() { Name = row.Key }); } } var i = 0; foreach (var pair in row) { var colname = pair.Key; var column_value = pair.Value; columnset[i].Name = colname; if (column_value == null) { columnset[i].Nullable = true; } else { columnset[i].Type = column_value.GetType(); } i++; } } for (int i = 0, c = columnset.Count; i < c; i++) { Type coltype = columnset[i].Type; if (coltype == null) { Console.WriteLine(columnset[i].Name + " no values, no type defined! Default: nullable string?"); columnset[i].Type = typeof(string); } } var orm = new ORMTableInfo(); // attributes var for_table_name = RemoveBrackets(table_name); orm.Name = ToValidNameRegex.Replace(for_table_name, "_"); orm.TableName = for_table_name; orm.As = As; orm.Title = ByViewRule(orm.Name, orm.Rules); // properties orm.Props = columnset.Select(row => { var property_name = ToValidNameRegex.Replace(row.Name, "_"); var value_type = row.Type; if ((value_type.IsValueType || value_type == typeof(DateTime)) && row.Nullable) { value_type = Type.GetType(value_type.CSTypeSyntax() + "?"); } var orm_pi = new ORMPropertyInfo(orm, property_name, row.Type) { Field = ByDBRule(property_name, orm.Rules), Title = ByViewRule(property_name, orm.Rules) }; return(orm_pi); }).ToArray(); orm.Keys = orm.Props.Where(prop => prop.isKey).ToArray(); orm.References = orm.Props.Where(prop => prop.RefType != null).ToArray(); return(orm); }
/// <summary> /// Build ORMTable data model source code /// </summary> /// <param name="orm">ORMTableInfo data model</param> /// <param name="output_type">Typ of output structure or class</param> /// <returns></returns> public static string GenORMTableTypeCode(ORMTableInfo orm, GenerateTypeNameEnum output_type = GenerateTypeNameEnum.Class) { ORMRulEnum current_rules = orm.Rules; var sb = new StringBuilder(); var for_table_name = RemoveBrackets(orm.TableName); if (orm.Name.Blank()) { throw new ArgumentException("Unassigned or invalid orm name"); } if (RemoveBrackets(orm.TableName).Blank()) { throw new ArgumentException("Unassigned or invalid orm table name"); } var generate_type_class = Enum.GetName(output_type).ToLower(); // Append attributes sb.AppendLine($"[ORMRuleSwitcher(ORMRulEnum.{Enum.GetName(current_rules & ORMRulEnum.__ViewMask) ?? "ViewHumanitaize"}, ORMRulEnum.{Enum.GetName(current_rules & ORMRulEnum.__DBMask) ?? "DBReplaceUnderscoresWithSpaces"})]"); var values = new List <string>(5); if (ByViewRule(orm.Name, current_rules) != orm.Title && orm.Title.notBlank()) { values.Add("Title = \"" + orm.Title + "\""); } if (for_table_name != ByDBRule(orm.Name, current_rules)) { values.Add((string)("TableName = \"" + for_table_name + "\"")); } if (orm.As.notBlank()) { values.Add("As = \"" + orm.As + "\""); } if (orm.TryGetIdPropertyName(out var id_property_name)) { values.Add("IdProperty = \"" + id_property_name + "\""); } if (orm.TextProperty.notBlank() == true) { values.Add("TextProperty = \"" + orm.TextProperty + "\""); } if (orm.Readonly) { values.Add("Readonly = true"); } if (values.Count == 0) { sb.AppendLine("[ORMTable]"); } else { sb.AppendLine($@"[ORMTable({string.Join(", ", values)})]"); } sb.AppendLine($"public partial {generate_type_class} {orm.Name} //::generated"); sb.AppendLine("{"); foreach (var orm_pi in orm.Props) { values.Clear(); var for_title = ByViewRule(orm_pi.Name, current_rules); if (for_title != orm_pi.Name || orm_pi.Title != orm_pi.Name) { values.Add($@"Title = ""{orm_pi.Title ?? for_title}"""); } var for_field = ByDBRule(orm_pi.Name, current_rules); if (for_field != orm_pi.Name || orm_pi.Field != orm_pi.Name) { values.Add($@"Field = ""{orm_pi.Field ?? for_field}"""); } if (orm_pi.Format.notBlank()) { values.Add($@"Format = {orm_pi.Format}"); } if (orm_pi.isKey) { values.Add($@"isKey = true"); } if (orm_pi.Readonly) { values.Add($@"Readonly = true"); } if (orm_pi.Hide) { values.Add($@"Hide = true"); } if (orm_pi.RefType != null) { values.Add($@"RefType = typeof({orm_pi.RefType.CSTypeSyntax()})"); } if (values.Count > 0) { sb.AppendLine($" [ORMProperty({string.Join(", ", values)})]"); } sb.AppendLine($" public {orm_pi.Type.CSTypeSyntax()} {orm_pi.Name} {{ get; set; }}"); } sb.AppendLine("}"); return(sb.ToString()); }
/// <summary> /// Link types only for reference to existing type, /// this link may be incorrect due to duplicate names or assembly binding is incorrect. /// Eerrors will be detected during compilation of builded souce code and runtime. /// If referebce target ORM type hasn't created yet /// need a second pass with existing types to bind. /// By default reference may writed as string type. /// Expensive. /// </summary> /// <param name="conn"></param> /// <param name="table_name">name of database table</param> /// <param name="assemblies">assemblies for metadata reference resolving</param> /// <param name="parent_list">optional list of ORMTableInfo for iteration and reqursive calling</param> /// <returns></returns> public static IList <ORMTableInfo> ORMFromSCHEMA(IDbConnection conn, string table_name, IEnumerable <Assembly> assemblies, IList <ORMTableInfo> parent_list = null) { var result = new List <ORMTableInfo>(); var orm_table_name = RemoveBrackets(table_name); var orm_name = ToValidNameRegex.Replace(orm_table_name, "_"); var already_exist = parent_list?.FirstOrDefault(orm => RemoveBrackets(orm.TableName) == orm_table_name); if (already_exist != null) { result.Add(already_exist); return(result); } // Construct orm & attribytes ORMTableInfo.SetDefaultDBProvider(DBProviderEnum.MSSql); var orm = new ORMTableInfo() { Name = orm_name, TableName = table_name, As = orm_name.ToLower() }; try { // types linking to existing type only for reference // this link may be incorrect due to duplicate names or assembly binding is incorrect // errors will be detected during compilation of builded souce code and runtime // try by table name foreach (var assembly in assemblies) { var types = assembly.GetTypes(); foreach (var type in types) { var ormtable_attr = type.GetCustomAttribute <ORMTableAttribute>(); if (ormtable_attr != null && RemoveBrackets(ormtable_attr.TableName) == table_name) { orm.Type = type; break; } } } // try by structure or class name foreach (var assembly in assemblies) { var types = assembly.GetTypes(); foreach (var type in types) { var ormtable_attr = type.GetCustomAttribute <ORMTableAttribute>(); if (ormtable_attr != null && (type.Name == table_name || type.Name == ToValidNameRegex.Replace(table_name, "_")) && ormtable_attr.TableName.Blank()) { orm.Type = type; break; } } } } catch (Exception e) { Console.WriteLine($@"Exception during explore metadata for resolving orm type for {orm.Name}"); Console.WriteLine(e.Message); } result.Add(orm); // load schema var schema_columns = conn.Query <SCHEMA_COLUMNS>($@"select * from INFORMATION_SCHEMA.COLUMNS"); var columns = schema_columns.Where(row => row.TABLE_NAME.Trim() == table_name); var schema_pks = conn.Query <sp_pkeys>($@"EXEC sp_pkeys [{table_name}]"); var schema_fks = conn.Query <sp_foreign_keys_rowset2>($@"EXEC sp_foreign_keys_rowset2 [{table_name}]"); // construct orm properties var properties = new List <ORMPropertyInfo>(); foreach (var row in columns) { // orm property attributes var type = ParseDataType(row.DATA_TYPE); var name = ToValidNameRegex.Replace(row.COLUMN_NAME, "_"); var title = ByViewRule(row.COLUMN_NAME, orm.Rules); var field = ByDBRule(row.COLUMN_NAME, orm.Rules); var orm_pi = new ORMPropertyInfo(orm, name, type) { Title = title, Field = field }; // from schema orm_pi.isKey = schema_pks.Any(row => row.COLUMN_NAME == field); var target_table_name = (schema_fks.FirstOrDefault(row => row.FK_COLUMN_NAME == field))?.PK_TABLE_NAME; if (target_table_name != null) { // create referenced orm info types var orm_list = ORMFromSCHEMA(conn, target_table_name, assemblies, result); result.AddRange(orm_list.Where(orm => !result.Contains(orm))); var ref_target_orm = result.FirstOrDefault(orm => RemoveBrackets(orm.TableName) == target_table_name); if (ref_target_orm == null) { ref_target_orm = result.FirstOrDefault(orm => (orm.Name == target_table_name || type.Name == ToValidNameRegex.Replace(table_name, "_"))); } if (ref_target_orm != null && ref_target_orm.Type != null) { // check ref target type exists orm_pi.RefType = ref_target_orm.Type; // there: // link type only for reference to existing type // this link may be incorrect due to duplicate names or assembly binding is incorrect // errors will be detected during compilation of builded souce code } if (orm_pi.RefType == null) { Console.WriteLine($@"Unresolved target orm type for reference property {orm.Name}.{orm_pi.Name}"); // The target ORM type hasn't created yet // need a second pass with existing types to bind. // For default write as string: orm_pi.RefType = typeof(string); } } properties.Add(orm_pi); } orm.Props = properties.ToArray(); orm.Keys = properties.Where(prop => prop.isKey).ToArray(); orm.References = properties.Where(prop => prop.RefType != null).ToArray(); return(result); }