Skip to content

Commit 13f47ff

Browse files
committed
New changes
1 parent c749625 commit 13f47ff

21 files changed

Lines changed: 1746 additions & 9 deletions

src/DbApiBuilderEntityGenerator.Core/CodeGenerator.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
22
using DbApiBuilderEntityGenerator.Core.Extensions;
3+
using DbApiBuilderEntityGenerator.Core.Metadata.Generation;
34
using DbApiBuilderEntityGenerator.Core.Options;
5+
using DbApiBuilderEntityGenerator.Core.Scripts;
46
using Microsoft.EntityFrameworkCore.Design;
57
using Microsoft.EntityFrameworkCore.Scaffolding;
68
using Microsoft.EntityFrameworkCore.Scaffolding.Metadata;
@@ -38,8 +40,42 @@ public bool Generate(GeneratorOptions options)
3840

3941
var context = _modelGenerator.Generate(Options, databaseModel, databaseProviders.mapping);
4042

43+
GenerateContextScriptTemplates(context);
44+
4145
return true;
4246
}
47+
48+
private void GenerateContextScriptTemplates(EntityContext entityContext)
49+
{
50+
var templateOption = new TemplateOptions();
51+
templateOption.Directory = Environment.CurrentDirectory;
52+
templateOption.FileName = "yaml-entity-context.yaml";
53+
templateOption.TemplatePath = Path.Combine(Environment.CurrentDirectory, "yaml-entity.csx");
54+
if (!VerifyScriptTemplate(templateOption))
55+
return;
56+
57+
try
58+
{
59+
var template = new ContextScriptTemplate(_loggerFactory, Options, templateOption);
60+
template.RunScript(entityContext);
61+
}
62+
catch (Exception ex)
63+
{
64+
_logger.LogError(ex, "Error Running Context Template: {message}", ex.Message);
65+
}
66+
}
67+
68+
private bool VerifyScriptTemplate(TemplateOptions templateOption)
69+
{
70+
var templatePath = templateOption.TemplatePath;
71+
// var templatePath = Path.Combine(templateOption.Directory, templateOption.FileName);
72+
73+
if (File.Exists(templatePath))
74+
return true;
75+
76+
_logger.LogWarning("Template '{template}' could not be found.", templatePath);
77+
return false;
78+
}
4379
private DatabaseModel GetDatabaseModel(IDatabaseModelFactory factory)
4480
{
4581
_logger.LogInformation("Loading database model ...");

src/DbApiBuilderEntityGenerator.Core/ConfigurationSerializer.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ public ConfigurationSerializer(ILogger<ConfigurationSerializer> logger)
2727
/// </summary>
2828
public const string OptionsFileName = "sample.yaml";
2929

30+
31+
public const string OutputFileName = "data-config.yaml";
32+
33+
/// <summary>
34+
/// The options file name. Default 'generation.yml'
35+
/// </summary>
36+
public const string TemplateFilePath = "DbApiBuilderEntityGenerator.template.config-template.csx";
37+
3038
/// <summary>
3139
/// Loads the options file using the specified <paramref name="directory"/> and <paramref name="file"/>.
3240
/// </summary>

src/DbApiBuilderEntityGenerator.Core/DbApiBuilderEntityGenerator.Core.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
<ItemGroup>
2727
<Folder Include="Metadata\" />
2828
<Folder Include="Metadata\Generation\" />
29+
<Folder Include="Scripts\" />
2930
<Folder Include="Serialization\" />
31+
<Folder Include="template\" />
3032
</ItemGroup>
33+
<ItemGroup>
34+
<EmbeddedResource Include="template\dab-config.csx" />
35+
</ItemGroup>
36+
3137
</Project>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Reflection;
3+
4+
namespace DbApiBuilderEntityGenerator.Core.Extensions;
5+
6+
public static class AssemblyExtensions
7+
{
8+
public static async Task<string> ReadResourceAsync(this Assembly assembly, string name)
9+
{
10+
// Determine path
11+
string resourcePath; // Format: "{Namespace}.{Folder}.{filename}.{Extension}"
12+
resourcePath = assembly.GetManifestResourceNames().Single(str => str.EndsWith(name));
13+
14+
using Stream stream = assembly.GetManifestResourceStream(resourcePath)!;
15+
using StreamReader reader = new(stream);
16+
return await reader.ReadToEndAsync();
17+
}
18+
}
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
using System;
2+
using System.Diagnostics.CodeAnalysis;
3+
using System.Text;
4+
5+
namespace DbApiBuilderEntityGenerator.Core.Extensions;
6+
7+
public static class GenerationExtensions
8+
{
9+
#region Data
10+
private static readonly HashSet<string> _csharpKeywords = new(StringComparer.Ordinal)
11+
{
12+
"as", "do", "if", "in", "is",
13+
"for", "int", "new", "out", "ref", "try",
14+
"base", "bool", "byte", "case", "char", "else", "enum", "goto", "lock", "long", "null", "this", "true", "uint", "void",
15+
"break", "catch", "class", "const", "event", "false", "fixed", "float", "sbyte", "short", "throw", "ulong", "using", "while",
16+
"double", "extern", "object", "params", "public", "return", "sealed", "sizeof", "static", "string", "struct", "switch", "typeof", "unsafe", "ushort",
17+
"checked", "decimal", "default", "finally", "foreach", "private", "virtual",
18+
"abstract", "continue", "delegate", "explicit", "implicit", "internal", "operator", "override", "readonly", "volatile",
19+
"__arglist", "__makeref", "__reftype", "interface", "namespace", "protected", "unchecked",
20+
"__refvalue", "stackalloc"
21+
};
22+
23+
private static readonly HashSet<string> _defaultNamespaces =
24+
[
25+
"System",
26+
"System.Collections.Generic",
27+
];
28+
29+
private static readonly Dictionary<Type, string> _csharpTypeAlias = new(16)
30+
{
31+
{ typeof(bool), "bool" },
32+
{ typeof(byte), "byte" },
33+
{ typeof(char), "char" },
34+
{ typeof(decimal), "decimal" },
35+
{ typeof(double), "double" },
36+
{ typeof(float), "float" },
37+
{ typeof(int), "int" },
38+
{ typeof(long), "long" },
39+
{ typeof(object), "object" },
40+
{ typeof(sbyte), "sbyte" },
41+
{ typeof(short), "short" },
42+
{ typeof(string), "string" },
43+
{ typeof(uint), "uint" },
44+
{ typeof(ulong), "ulong" },
45+
{ typeof(ushort), "ushort" },
46+
{ typeof(void), "void" }
47+
};
48+
#endregion
49+
50+
public static string ToFieldName(this string name)
51+
{
52+
ArgumentException.ThrowIfNullOrEmpty(name);
53+
54+
return "_" + name.ToCamelCase();
55+
}
56+
57+
public static string MakeUnique(this string name, Func<string, bool> exists)
58+
{
59+
ArgumentException.ThrowIfNullOrEmpty(name);
60+
ArgumentNullException.ThrowIfNull(exists);
61+
62+
string uniqueName = name;
63+
int count = 1;
64+
65+
while (exists(uniqueName))
66+
uniqueName = string.Concat(name, count++);
67+
68+
return uniqueName;
69+
}
70+
71+
public static bool IsKeyword(this string text)
72+
{
73+
ArgumentException.ThrowIfNullOrEmpty(text);
74+
75+
return _csharpKeywords.Contains(text);
76+
}
77+
78+
[return: NotNullIfNotNull(nameof(name))]
79+
public static string? ToSafeName(this string? name)
80+
{
81+
if (string.IsNullOrEmpty(name))
82+
return name;
83+
84+
if (!name.IsKeyword())
85+
return name;
86+
87+
return "@" + name;
88+
}
89+
90+
public static string ToType(this Type type)
91+
{
92+
ArgumentNullException.ThrowIfNull(type);
93+
94+
var stringBuilder = new StringBuilder();
95+
ProcessType(stringBuilder, type);
96+
return stringBuilder.ToString();
97+
}
98+
99+
public static string? ToNullableType(this Type type, bool isNullable = false)
100+
{
101+
bool isValueType = type.IsValueType;
102+
103+
var typeString = type.ToType();
104+
105+
if (!isValueType || !isNullable)
106+
return typeString;
107+
108+
return typeString.EndsWith('?') ? typeString : typeString + "?";
109+
}
110+
111+
public static bool IsValueType(this string? type)
112+
{
113+
if (string.IsNullOrEmpty(type))
114+
return false;
115+
116+
if (!type.StartsWith("System."))
117+
return false;
118+
119+
var t = Type.GetType(type, false);
120+
return t != null && t.IsValueType;
121+
}
122+
123+
public static string ToLiteral(this string value)
124+
{
125+
ArgumentException.ThrowIfNullOrEmpty(value);
126+
127+
return value.Contains('\n') || value.Contains('\r')
128+
? "@\"" + value.Replace("\"", "\"\"") + "\""
129+
: "\"" + value.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
130+
}
131+
132+
133+
134+
private static void ProcessType(StringBuilder builder, Type type)
135+
{
136+
if (type.IsGenericType)
137+
{
138+
var genericArguments = type.GetGenericArguments();
139+
ProcessGenericType(builder, type, genericArguments, genericArguments.Length);
140+
}
141+
else if (type.IsArray)
142+
{
143+
ProcessArrayType(builder, type);
144+
}
145+
else if (_csharpTypeAlias.TryGetValue(type, out var builtInName))
146+
{
147+
builder.Append(builtInName);
148+
}
149+
else if (type.Namespace.HasValue() && _defaultNamespaces.Contains(type.Namespace))
150+
{
151+
builder.Append(type.Name);
152+
}
153+
else
154+
{
155+
builder.Append(type.FullName ?? type.Name);
156+
}
157+
}
158+
159+
private static void ProcessArrayType(StringBuilder builder, Type type)
160+
{
161+
var innerType = type;
162+
while (innerType.IsArray)
163+
{
164+
innerType = innerType.GetElementType()!;
165+
}
166+
167+
ProcessType(builder, innerType);
168+
169+
while (type.IsArray)
170+
{
171+
builder.Append('[');
172+
builder.Append(',', type.GetArrayRank() - 1);
173+
builder.Append(']');
174+
type = type.GetElementType()!;
175+
}
176+
}
177+
178+
private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length)
179+
{
180+
if (type.IsConstructedGenericType
181+
&& type.GetGenericTypeDefinition() == typeof(Nullable<>))
182+
{
183+
ProcessType(builder, type.GetUnderlyingType());
184+
builder.Append('?');
185+
return;
186+
}
187+
188+
var offset = type.DeclaringType != null ? type.DeclaringType.GetGenericArguments().Length : 0;
189+
var genericPartIndex = type.Name.IndexOf('`');
190+
if (genericPartIndex <= 0)
191+
{
192+
if (type.Namespace.HasValue() && _defaultNamespaces.Contains(type.Namespace))
193+
{
194+
builder.Append(type.Name);
195+
}
196+
else
197+
{
198+
builder.Append(type.FullName ?? type.Name);
199+
}
200+
return;
201+
}
202+
203+
if (type.Namespace.HasValue() && !_defaultNamespaces.Contains(type.Namespace))
204+
{
205+
builder.Append(type.Namespace);
206+
builder.Append(".");
207+
}
208+
builder.Append(type.Name, 0, genericPartIndex);
209+
builder.Append('<');
210+
211+
for (var i = offset; i < length; i++)
212+
{
213+
ProcessType(builder, genericArguments[i]);
214+
if (i + 1 == length)
215+
{
216+
continue;
217+
}
218+
219+
builder.Append(',');
220+
if (!genericArguments[i + 1].IsGenericParameter)
221+
{
222+
builder.Append(' ');
223+
}
224+
}
225+
226+
builder.Append('>');
227+
}
228+
229+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
3+
namespace DbApiBuilderEntityGenerator.Core.Extensions;
4+
5+
public static class TypeExtensions
6+
{
7+
/// <summary>
8+
/// Gets the underlying type dealing with <see cref="T:Nullable`1"/>.
9+
/// </summary>
10+
/// <param name="type">The type.</param>
11+
/// <returns>Returns a type dealing with <see cref="T:Nullable`1"/>.</returns>
12+
public static Type GetUnderlyingType(this Type type)
13+
{
14+
ArgumentNullException.ThrowIfNull(type);
15+
return Nullable.GetUnderlyingType(type) ?? type;
16+
}
17+
18+
/// <summary>
19+
/// Determines whether the specified <paramref name="type"/> can be null.
20+
/// </summary>
21+
/// <param name="type">The type to check.</param>
22+
/// <returns>
23+
/// <c>true</c> if the specified <paramref name="type"/> can be null; otherwise, <c>false</c>.
24+
/// </returns>
25+
public static bool IsNullable(this Type type)
26+
{
27+
ArgumentNullException.ThrowIfNull(type);
28+
29+
if (!type.IsGenericType || type.IsGenericTypeDefinition)
30+
return false;
31+
32+
// Instantiated generic type only
33+
Type genericType = type.GetGenericTypeDefinition();
34+
return ReferenceEquals(genericType, typeof(Nullable<>));
35+
}
36+
37+
}

src/DbApiBuilderEntityGenerator.Core/Options/GeneratorOptions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,13 @@ public string? Directory
123123
/// </value>
124124
public SelectionOptions Renaming { get; }
125125

126+
/// <summary>
127+
/// Gets or sets the renaming expressions.
128+
/// </summary>
129+
/// <value>
130+
/// The renaming expressions.
131+
/// </value>
132+
public string? OutputFileName { get; set; }
133+
134+
public string? TemplateFilePath { get; set; }
126135
}

src/DbApiBuilderEntityGenerator.Core/Options/OptionsBase.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ public OptionsBase()
1616
{
1717

1818
}
19+
public OptionsBase(VariableDictionary variables)
20+
{
21+
Variables = variables;
22+
}
23+
1924
public VariableDictionary Variables { get; } = new VariableDictionary();
2025

2126
public string? Prefix { get; }

0 commit comments

Comments
 (0)