Skip to content

Commit 0cc604a

Browse files
authored
Merge pull request #1 from InvaderZim85/feature/EfCoreKeyCode
EF Key Code, Column information, Update check
2 parents cf4672d + 6ef4558 commit 0cc604a

File tree

20 files changed

+744
-32
lines changed

20 files changed

+744
-32
lines changed

MsSql.ClassGenerator.Cli/Program.cs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
using System.Diagnostics;
2-
using System.Reflection;
3-
using MsSql.ClassGenerator.Cli.Model;
1+
using MsSql.ClassGenerator.Cli.Model;
42
using MsSql.ClassGenerator.Core.Business;
53
using MsSql.ClassGenerator.Core.Common;
64
using MsSql.ClassGenerator.Core.Model;
75
using Serilog;
8-
using Serilog.Events;
6+
using System.Diagnostics;
7+
using System.Reflection;
98

109
namespace MsSql.ClassGenerator.Cli;
1110

@@ -21,6 +20,8 @@ internal static class Program
2120
/// <returns>The awaitable task.</returns>
2221
private static async Task Main(string[] args)
2322
{
23+
await CheckForUpdate();
24+
2425
var argResult = args.ExtractArguments(out Arguments arguments);
2526

2627
Helper.InitLog(arguments.LogLevel, true);
@@ -98,4 +99,33 @@ private static void PrintFooterHeader(bool header)
9899
if (header)
99100
Log.Information("Version: {version}", Assembly.GetExecutingAssembly().GetName().Version);
100101
}
102+
103+
/// <summary>
104+
/// Checks if an update is available.
105+
/// </summary>
106+
/// <returns>The awaitable task.</returns>
107+
private static async Task CheckForUpdate()
108+
{
109+
try
110+
{
111+
await UpdateHelper.LoadReleaseInfoAsync(releaseInfo =>
112+
{
113+
var updateMessage = $"Update available. New version: {releaseInfo.NewVersion}";
114+
const string link = $"Link: {UpdateHelper.GitHupUrl}";
115+
116+
var maxLength = updateMessage.Length > link.Length ? updateMessage.Length : link.Length;
117+
118+
var line = "-".PadRight(maxLength + 2, '-'); // Add two for the spacer
119+
Console.WriteLine($"+{line}+");
120+
Console.WriteLine($"| {updateMessage.PadRight(maxLength, ' ')} |");
121+
Console.WriteLine($"| {link.PadRight(maxLength, ' ')} |");
122+
Console.WriteLine($"+{line}+");
123+
Console.WriteLine();
124+
});
125+
}
126+
catch
127+
{
128+
// Ignore
129+
}
130+
}
101131
}

MsSql.ClassGenerator.Core/Business/ClassManager.cs

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using MsSql.ClassGenerator.Core.Common;
1+
using System.Reflection.Emit;
2+
using System.Text;
3+
using MsSql.ClassGenerator.Core.Common;
24
using MsSql.ClassGenerator.Core.Model;
35
using Serilog;
46

@@ -12,7 +14,15 @@ public partial class ClassManager
1214
/// <summary>
1315
/// Occurs when progress was made.
1416
/// </summary>
15-
public event EventHandler<string>? ProgressEvent;
17+
public event EventHandler<string>? ProgressEvent;
18+
19+
/// <summary>
20+
/// Gets the EF Key code.
21+
/// </summary>
22+
/// <remarks>
23+
/// <b>Note</b>: The code is only generated when the option <see cref="ClassGeneratorOptions.DbModel"/> is set to <see langword="true"/>.
24+
/// </remarks>
25+
public EfKeyCodeResult EfKeyCode { get; private set; } = new();
1626

1727
/// <summary>
1828
/// Generates the classes out of the specified tables according to the specified options.
@@ -23,6 +33,8 @@ public partial class ClassManager
2333
/// <exception cref="DirectoryNotFoundException">Will be thrown when the specified output directory doesn't exist.</exception>
2434
public async Task GenerateClassAsync(ClassGeneratorOptions options, List<TableEntry> tables)
2535
{
36+
EfKeyCode = new EfKeyCodeResult();
37+
2638
// Step 0: Check the options.
2739
if (!Directory.Exists(options.Output))
2840
throw new DirectoryNotFoundException($"The specified output ({options.Output}) folder doesn't exist.");
@@ -40,6 +52,10 @@ public async Task GenerateClassAsync(ClassGeneratorOptions options, List<TableEn
4052
Log.Debug("Generate class for table '{name}'...", table.Name);
4153
await GenerateClassAsync(options, table);
4254
}
55+
56+
// Generate the EF Key code
57+
if (options.DbModel)
58+
GenerateEfKeyCode(tables);
4359
}
4460

4561
/// <summary>
@@ -248,4 +264,70 @@ private static string GetPropertyAttributes(ClassGeneratorOptions options, Colum
248264

249265
return string.Join(Environment.NewLine, attributes.OrderBy(o => o.Key).Select(s => s.Value));
250266
}
267+
268+
/// <summary>
269+
/// Generates the EF Key code.
270+
/// </summary>
271+
/// <param name="tables">The list with the tables.</param>
272+
private void GenerateEfKeyCode(List<TableEntry> tables)
273+
{
274+
// Get all tables which contains more than one key column
275+
var tmpTables = tables.Where(w => w.Columns.Count(c => c.IsPrimaryKey) > 1).ToList();
276+
if (tmpTables.Count == 0)
277+
return;
278+
279+
var sb = PrepareStringBuilder();
280+
var count = 1;
281+
foreach (var tableEntry in tmpTables)
282+
{
283+
// Add the entity
284+
sb.AppendLine($"{Tab}modelBuilder.Entity<{tableEntry.ClassName}>().HasKey(k => new")
285+
.AppendLine($"{Tab}{{");
286+
287+
// Get the key columns
288+
var columnCount = 1;
289+
var columns = tableEntry.Columns.Where(w => w.IsPrimaryKey).ToList();
290+
foreach (var columnEntry in columns)
291+
{
292+
var comma = columnCount++ != columns.Count ? "," : string.Empty;
293+
294+
sb.AppendLine($"{Tab}{Tab}k.{columnEntry.PropertyName}{comma}");
295+
}
296+
297+
// Add the closing brackets
298+
sb.AppendLine($"{Tab}}});");
299+
300+
if (count++ != tmpTables.Count)
301+
sb.AppendLine(); // Spacer
302+
303+
}
304+
305+
EfKeyCode = new EfKeyCodeResult
306+
{
307+
Code = FinalizeStringBuilder(),
308+
TableCount = tmpTables.Count
309+
};
310+
311+
return;
312+
313+
StringBuilder PrepareStringBuilder()
314+
{
315+
var stringBuilder = new StringBuilder()
316+
.AppendLine("/// <inheritdoc />")
317+
.AppendLine("protected override void OnModelCreating(ModelBuilder modelBuilder)")
318+
.AppendLine("{");
319+
320+
return stringBuilder;
321+
}
322+
323+
// Adds the final code
324+
string FinalizeStringBuilder()
325+
{
326+
sb.AppendLine("}");
327+
328+
return sb.ToString();
329+
}
330+
}
331+
332+
251333
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using MsSql.ClassGenerator.Core.Model;
2+
using RestSharp;
3+
using RestSharp.Serializers.NewtonsoftJson;
4+
using Serilog;
5+
using System.Reflection;
6+
7+
namespace MsSql.ClassGenerator.Core.Business;
8+
9+
/// <summary>
10+
/// Provides the functions for the update.
11+
/// </summary>
12+
public static class UpdateHelper
13+
{
14+
/// <summary>
15+
/// Contains the URL of the latest release of the app.
16+
/// </summary>
17+
public const string GitHupUrl = "https://github.com/InvaderZim85/MsSql.ClassGenerator/releases/latest";
18+
19+
/// <summary>
20+
/// Contains the URL of the latest release for the REST call.
21+
/// </summary>
22+
private const string GitHupApiUrl =
23+
"https://api.github.com/repos/InvaderZim85/MsSql.ClassGenerator/releases/latest";
24+
25+
/// <summary>
26+
/// Loads the release info of the latest release to determine if there is a new version.
27+
/// </summary>
28+
/// <param name="callback">Will be executed when there is a new version.</param>
29+
/// <returns>The awaitable task.</returns>
30+
public static async Task LoadReleaseInfoAsync(Action<ReleaseInfo> callback)
31+
{
32+
try
33+
{
34+
var client = new RestClient(GitHupApiUrl,
35+
configureSerialization: s => s.UseNewtonsoftJson());
36+
37+
client.AddDefaultHeader("accept", "application/vnd.github.v3+json");
38+
39+
var request = new RestRequest();
40+
var response = await client.GetAsync<ReleaseInfo>(request);
41+
42+
// This method also checks if the response is null
43+
if (IsNewVersionAvailable(response))
44+
callback(response!);
45+
}
46+
catch (Exception ex)
47+
{
48+
Log.Warning(ex, "Error while loading the latest release info.");
49+
}
50+
}
51+
52+
/// <summary>
53+
/// Checks if an update is available
54+
/// </summary>
55+
/// <param name="releaseInfo">The infos of the latest release</param>
56+
/// <returns><see langword="true"/> when there is a new version, otherwise <see langword="false"/></returns>
57+
private static bool IsNewVersionAvailable(ReleaseInfo? releaseInfo)
58+
{
59+
if (releaseInfo == null)
60+
return false;
61+
62+
if (!Version.TryParse(releaseInfo.TagName.Replace("v", ""), out var releaseVersion))
63+
{
64+
Log.Warning("Can't determine version of the latest release. Tag value: {value}", releaseInfo.TagName);
65+
return false;
66+
}
67+
68+
var currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
69+
if (currentVersion == null)
70+
return false;
71+
72+
releaseInfo.CurrentVersion = currentVersion;
73+
releaseInfo.NewVersion = releaseVersion;
74+
75+
return releaseInfo.UpdateAvailable;
76+
}
77+
}

MsSql.ClassGenerator.Core/Common/Extensions.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,24 @@ public static bool StartsWithNumber(this string value)
118118
{
119119
return !string.IsNullOrWhiteSpace(value) && int.TryParse(value[0].ToString(), out _);
120120
}
121+
122+
/// <summary>
123+
/// Converts the value into a readable size
124+
/// </summary>
125+
/// <param name="value">The value</param>
126+
/// <param name="divider">The divider (optional)</param>
127+
/// <returns>The converted size</returns>
128+
public static string ConvertSize(this long value, int divider = 1024)
129+
{
130+
return value switch
131+
{
132+
_ when value < divider => $"{value:N0} Bytes",
133+
_ when value >= divider && value < Math.Pow(divider, 2) => $"{value / divider:N2} KB",
134+
_ when value >= Math.Pow(divider, 2) && value < Math.Pow(divider, 3) =>
135+
$"{value / Math.Pow(divider, 2):N2} MB",
136+
_ when value >= Math.Pow(divider, 3) && value <= Math.Pow(divider, 4) => $"{value / Math.Pow(divider, 3):N2} GB",
137+
_ when value >= Math.Pow(divider, 4) => $"{value / Math.Pow(divider, 4)} TB",
138+
_ => value.ToString("N0")
139+
};
140+
}
121141
}

MsSql.ClassGenerator.Core/Common/Helper.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Newtonsoft.Json;
22
using Serilog;
33
using Serilog.Events;
4+
using System.Diagnostics;
45

56
namespace MsSql.ClassGenerator.Core.Common;
67

@@ -90,4 +91,21 @@ public static List<string> GetModifierList()
9091
"protected internal"
9192
];
9293
}
94+
95+
/// <summary>
96+
/// Opens the specified link
97+
/// </summary>
98+
/// <param name="url">The url of the link</param>
99+
public static void OpenLink(string url)
100+
{
101+
try
102+
{
103+
url = url.Replace("&", "^&");
104+
Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true });
105+
}
106+
catch
107+
{
108+
// Ignore
109+
}
110+
}
93111
}

MsSql.ClassGenerator.Core/Data/BaseRepo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ protected SqlConnection GetConnection()
3030
DataSource = server,
3131
IntegratedSecurity = true,
3232
TrustServerCertificate = true,
33-
ApplicationName = nameof(MsSql.ClassGenerator)
33+
ApplicationName = nameof(ClassGenerator)
3434
};
3535

3636
if (!string.IsNullOrWhiteSpace(database))
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using MsSql.ClassGenerator.Core.Common;
2+
using Newtonsoft.Json;
3+
4+
namespace MsSql.ClassGenerator.Core.Model;
5+
6+
/// <summary>
7+
/// Represents the assets of the last release
8+
/// </summary>
9+
public sealed class Asset
10+
{
11+
/// <summary>
12+
/// Gets or sets the name of the zip file
13+
/// </summary>
14+
public string Name { get; set; } = string.Empty;
15+
16+
/// <summary>
17+
/// Gets or sets the size of the latest release (in bytes)
18+
/// </summary>
19+
public long Size { get; set; }
20+
21+
/// <summary>
22+
/// Gets the size of the latest release in a readable format
23+
/// </summary>
24+
[JsonIgnore]
25+
public string SizeView => Size.ConvertSize();
26+
27+
/// <summary>
28+
/// Gets or sets the url of the release
29+
/// </summary>
30+
[JsonProperty("browser_download_url")]
31+
public string DownloadUrl { get; set; } = string.Empty;
32+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace MsSql.ClassGenerator.Core.Model;
2+
3+
/// <summary>
4+
/// Provides the key code result.
5+
/// </summary>
6+
public sealed class EfKeyCodeResult
7+
{
8+
/// <summary>
9+
/// Gets the code.
10+
/// </summary>
11+
public string Code { get; init; } = string.Empty;
12+
13+
/// <summary>
14+
/// Gets the amount of tables which contains multiple keys.
15+
/// </summary>
16+
public int TableCount { get; init; }
17+
18+
/// <summary>
19+
/// Gets the value which indicates whether the code is empty.
20+
/// </summary>
21+
public bool IsEmpty => string.IsNullOrWhiteSpace(Code);
22+
}

0 commit comments

Comments
 (0)