Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\NonSucking.Framework.Extension.EntityFrameworkCore\NonSucking.Framework.Extension.EntityFrameworkCore.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using NonSucking.Framework.Extension.EntityFrameworkCore.MigrationBuilder;
using System.CommandLine;
using System.CommandLine.Builder;
using System.CommandLine.Parsing;

var sourceOption
= new Option<FileInfo>(
new[] { "--source", "-s" },
(argumentResult) => new FileInfo(argumentResult.Tokens[0].Value),
description: "Defines the source .dll to search for migration data"
);

var targetOption
= new Option<DirectoryInfo>(
new[] { "--target", "-t" },
(argumentResult) => new DirectoryInfo(argumentResult.Tokens[0].Value),
description: "Defines the directory where the migration should be generated"
);

var root = new RootCommand("search dll for DatabaseContext and generate Migration files")
{
sourceOption,
targetOption
};

root.SetHandler(Service.Start, sourceOption, targetOption);

var parser = new CommandLineBuilder(root).UseDefaults().Build();

ParseResult parsedArguments;

if (args is null || args.Length == 0)
{
Console.Write("> ");
var clr = Console.ReadLine();
parsedArguments = parser.Parse(clr);
}
else
{
parsedArguments = parser.Parse(args);
}

if (parsedArguments.Errors.Count > 0 && parsedArguments.Tokens.Count > 0)
{
Console.WriteLine($"{parsedArguments.Errors.Count} Errors in the parse process.");
foreach (var error in parsedArguments.Errors)
{
Console.WriteLine(error.Message);
}

parser.Invoke("-?");

return 1;
}
else
{
return parsedArguments.Invoke();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"profiles": {
"NonSucking.Framework.Extension.EntityFrameworkCore.MigrationBuilder": {
"commandName": "Project",
"commandLineArgs": "-s \"F:\\Projekte\\Visual 2019\\NonSucking.Framework.Extension\\TestCoreExtension\\bin\\Debug\\net7.0\\TestCoreExtension.dll\" -t \"F:\\Projekte\\Visual 2019\\NonSucking.Framework.Extension\\TestCoreExtension\\Migration\""
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.EntityFrameworkCore;
using NonSucking.Framework.Extension.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;

namespace NonSucking.Framework.Extension.EntityFrameworkCore.MigrationBuilder;
internal static class Service
{
public static void Start(FileInfo sourceFile, DirectoryInfo targetDirectory)
{
var context = new AssemblyLoadContext("sourceContext");

using var stream = sourceFile.OpenRead();

context.LoadFromStream(stream);

var types
= context
.Assemblies
.SelectMany(x => x.GetTypes())
.Where(type => !type.IsAbstract && !type.IsInterface && type.IsAssignableTo(typeof(IEntity)))
.Where(type => type.GetCustomAttribute<HistoryAttribute>() is null);

//Todo: create migration class and designer file
//requires implementation of IAutoMigrationTypeProvider
//Add: const Id field of patter Number_name example: 1_Initialized
foreach (var type in types)
{
//add types to designer file put History attribute on it
//example: [History(Migration.Id)]
foreach (var property in type.GetProperties())
{

}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

namespace NonSucking.Framework.Extension.EntityFrameworkCore
{

public abstract class DatabaseContext : DbContext, IAutoMigrationContext
{
public bool EnableUseLazyLoading { get; set; } = true;
Expand Down Expand Up @@ -63,31 +62,11 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
base.OnConfiguring(optionsBuilder);
}

static Type[] onModelCreatingMethodTypes = new[] { typeof(ModelBuilder) };
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
if (AddAllEntities)
{

foreach (var type in AssemblyLoadContext
.Default
.Assemblies
.Where(x => string.IsNullOrWhiteSpace(AssemblyRootName)
|| x.FullName.Contains(AssemblyRootName))
.SelectMany(x => x.GetTypes())
.Where(type => !type.IsAbstract
&& !type.IsInterface
&& typeof(IEntity).IsAssignableFrom(type))
.Where(x => x.Namespace is not null && !x.Namespace.Contains("Migration")))
{
if (modelBuilder.Model.FindEntityType(type) is null)
{
_ = modelBuilder.Model.AddEntityType(type);
}
var method = type.GetMethod(nameof(OnModelCreating), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public, onModelCreatingMethodTypes);
if (method is not null)
method.Invoke(null, new[] { modelBuilder });
}
modelBuilder.BuildCurrent(AssemblyRootName);
}

base.OnModelCreating(modelBuilder);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace NonSucking.Framework.Extension.EntityFrameworkCore.Migrations;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum, AllowMultiple = false, Inherited = true)]
public class HistoryAttribute : Attribute
{
/// <summary>
/// The version to migrate to
/// </summary>
public string Version { get; }

/// <summary>
/// Creates a custom migration config
/// </summary>
public HistoryAttribute()
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,54 @@
using Microsoft.EntityFrameworkCore.Migrations;

using System.Reflection;
using System.Runtime.Loader;

namespace NonSucking.Framework.Extension.EntityFrameworkCore.Migrations;

public static class ModelMigration
{
public static void BuildCurrent(this ModelBuilder modelBuilder, string assemblyRootName)
{
HashSet<Assembly> assemblies = new HashSet<Assembly>();
foreach (var type in GetEntityTypes(assemblyRootName)
.Where(type =>
type.GetCustomAttribute<HistoryAttribute>() is null
&& type.DeclaringType?.GetCustomAttribute<HistoryAttribute>() is null))
{
if (modelBuilder.Model.FindEntityType(type) is not null)
continue;

_ = modelBuilder.Model.AddEntityType(type);
assemblies.Add(type.Assembly);
}
foreach (var item in assemblies)
{
modelBuilder.ApplyConfigurationsFromAssembly(item);
}
}

public static void BuildVersion(this ModelBuilder modelBuilder, string version)
{
static bool HasCorrectVersion(Type type, string version)
{
var attribute
= type.GetCustomAttribute<HistoryAttribute>()
?? type.DeclaringType?.GetCustomAttribute<HistoryAttribute>();
return attribute is not null
&& attribute.Version == version;
}

foreach (var type in GetEntityTypes(null)
.Where<Type>(type => HasCorrectVersion(type, version)))
{
if (modelBuilder.Model.FindEntityType(type) is null)
_ = modelBuilder.Model.AddEntityType(type);
}
}


public static void BuildVersion(this ModelBuilder modelBuilder, IAutoMigrationTypeProvider typeProvider)
{
//TODO: Solution required our table names are property names from the context
foreach (var type in typeProvider.GetEntityTypes())
{
if (modelBuilder.Model.FindEntityType(type) is null)
Expand All @@ -43,13 +78,25 @@ public static void SetDowngradeOperations(this MigrationBuilder migrationBuilder
migrationBuilder.Operations.AddRange(diff);
}

private static IEnumerable<Type> GetEntityTypes(string? assemblyRootName)
{
return AssemblyLoadContext
.Default
.Assemblies
.Where(x => string.IsNullOrWhiteSpace(assemblyRootName)
|| x.FullName.Contains(assemblyRootName))
.SelectMany(x => x.GetTypes())
.Where(type => !type.IsAbstract && !type.IsInterface && type.IsAssignableTo(typeof(IEntity)));
}
private static void GetMigrationClasses(Migration migration, out IAutoMigrationContextBuilder providerContextBuilder, out IModel target, out IModel? source)
{
providerContextBuilder = DatabaseFactory.DatabaseConfigurators.First().GetEmptyForMigration();
var migrationType = migration.GetType();
var contextAttribute = migrationType.GetCustomAttribute<DbContextAttribute>() ?? throw new ArgumentNullException();
var currentContext = (IAutoMigrationContext)Activator.CreateInstance(contextAttribute.ContextType)!;
var builderFromConfig = DatabaseFactory.DatabaseConfigurators.FirstOrDefault()?.GetEmptyForMigration();

providerContextBuilder = builderFromConfig ?? (IAutoMigrationContextBuilder)Activator.CreateInstance(contextAttribute.ContextType)!;

var currentContext = (IAutoMigrationContext)Activator.CreateInstance(contextAttribute.ContextType)!;
var targetBuilder = providerContextBuilder.CreateBuilder();

if (migration is IAutoMigrationTypeProvider autoTypeProvider)
Expand All @@ -58,14 +105,16 @@ private static void GetMigrationClasses(Migration migration, out IAutoMigrationC
}
else
{
var idAttribute = migrationType.GetCustomAttribute<MigrationAttribute>() ??
throw new ArgumentNullException();
var idAttribute
= migrationType.GetCustomAttribute<MigrationAttribute>()
?? throw new ArgumentNullException();

targetBuilder.BuildVersion(idAttribute.Id);
}

target = providerContextBuilder.FinalizeModel((IModel)targetBuilder.Model);
source = null;

if (currentContext.FindLastMigration(contextAttribute.ContextType, out var lastMigration, out var lastMigrationId))
{
var sourceBuilder = providerContextBuilder.CreateBuilder();
Expand Down
12 changes: 12 additions & 0 deletions NonSucking.Framework.Extension.sln
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NonSucking.Framework.Extens
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NonSucking.Framework.Extension.Database.PostgreSQL", "EntityFrameworkExtension\NonSucking.Framework.Extension.Database.PostgreSQL\NonSucking.Framework.Extension.Database.PostgreSQL.csproj", "{3B88FE46-6E96-4597-AC05-60984F3B4FD0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NonSucking.Framework.Extension.EntityFrameworkCore.MigrationBuilder", "NonSucking.Framework.Extension.EntityFrameworkCore.MigrationBuilder\NonSucking.Framework.Extension.EntityFrameworkCore.MigrationBuilder.csproj", "{93086D2B-A9CC-461C-A9C7-6EDEA0635753}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestCoreExtension", "TestCoreExtension\TestCoreExtension.csproj", "{143FA061-260E-4EAB-A208-725342061E81}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -101,6 +105,14 @@ Global
{3B88FE46-6E96-4597-AC05-60984F3B4FD0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B88FE46-6E96-4597-AC05-60984F3B4FD0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B88FE46-6E96-4597-AC05-60984F3B4FD0}.Release|Any CPU.Build.0 = Release|Any CPU
{93086D2B-A9CC-461C-A9C7-6EDEA0635753}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{93086D2B-A9CC-461C-A9C7-6EDEA0635753}.Debug|Any CPU.Build.0 = Debug|Any CPU
{93086D2B-A9CC-461C-A9C7-6EDEA0635753}.Release|Any CPU.ActiveCfg = Release|Any CPU
{93086D2B-A9CC-461C-A9C7-6EDEA0635753}.Release|Any CPU.Build.0 = Release|Any CPU
{143FA061-260E-4EAB-A208-725342061E81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{143FA061-260E-4EAB-A208-725342061E81}.Debug|Any CPU.Build.0 = Debug|Any CPU
{143FA061-260E-4EAB-A208-725342061E81}.Release|Any CPU.ActiveCfg = Release|Any CPU
{143FA061-260E-4EAB-A208-725342061E81}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
23 changes: 23 additions & 0 deletions TestCoreExtension/Bothamster/BaseDatabaseContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore;

using NonSucking.Framework.Extension.EntityFrameworkCore;

namespace TestCoreExtension.Bothamster;

public class BaseDatabaseContext : DatabaseContext
{
public BaseDatabaseContext()
{
}

public BaseDatabaseContext(DbContextOptions options) : base(options)
{
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename =Test.db");

base.OnConfiguring(optionsBuilder);
}
}
24 changes: 24 additions & 0 deletions TestCoreExtension/Bothamster/Group.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@


using System.ComponentModel.DataAnnotations.Schema;

namespace TestCoreExtension.Bothamster;

[Table("Groups")]
public class Group : IdEntity<int>
{
public bool IsDefault { get; set; }
public string Name { get; set; }

[InverseProperty(nameof(User.Groups))]
public virtual List<User> Users { get; set; } = new List<User>();
[InverseProperty(nameof(PlattformUser.Groups))]
public virtual List<PlattformUser> PlattformUsers { get; set; } = new List<PlattformUser>();
[InverseProperty(nameof(Right.Groups))]
public virtual List<Right> Rights { get; set; } = new List<Right>();

public Group Clone()
{
return new Group { Id = Id, IsDefault = IsDefault, Name = Name, Users = Users.ToList(), PlattformUsers = PlattformUsers.ToList(), Rights = Rights.ToList() };
}
}
Loading