Skip to content

Commit

Permalink
Experimental work towards #266
Browse files Browse the repository at this point in the history
  • Loading branch information
ascott18 committed Dec 7, 2022
1 parent 28ce009 commit 132bd67
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
using IntelliTect.Coalesce.CodeGeneration.Utilities;
using IntelliTect.Coalesce.TypeDefinition;
using IntelliTect.Coalesce.Validation;
using Microsoft.CodeAnalysis.Differencing;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
Expand Down Expand Up @@ -107,6 +109,75 @@ public async Task GenerateAsync(Type rootGenerator)
types.Select(t => new SymbolTypeViewModel(rr, t))
);

#if NET5_0_OR_GREATER
ResolveEventHandler assemblyResolver = (sender, args) =>
{
var assemblyName = new AssemblyName(args.Name);
var allLoaded = AppDomain.CurrentDomain.GetAssemblies();
var alreadyLoaded = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == assemblyName.Name);
if (alreadyLoaded != null)
{
Logger.LogWarning($"Resolved {assemblyName.Name} to {alreadyLoaded.GetName().Version} ({assemblyName.Version} was requested by {genContext.DataProject.ProjectFileName})");
return alreadyLoaded;
}
foreach (var extension in new[] { ".dll", ".exe" })
{
var match = (genContext.DataProject as RoslynProjectContext).MsBuildProjectContext.CompilationAssemblies
.FirstOrDefault(a => a.Name == assemblyName.Name + extension);
if (match != null)
{
return Assembly.LoadFrom(match.ResolvedPath);
}
}
return null;
};

AppDomain.CurrentDomain.AssemblyResolve += assemblyResolver;
try
{
foreach (var dbContext in rr.DbContexts)
{
// WARNING: This stuff is extremely fragile and implodes on the slightest version mismatch.
// It currently works if you launch a .net6 tool against a .net6 target project (e.g. coakesce-vue2.json).
// We may need to: Launch a second executable of the generator, which since it will be a fresh process, won't have any EF assemblies loaded,
// so we can load the Data project and all its dependencies into that fresh process,
// then extract and serialize the EF model (the bits we carea bout) and send it back into the generator process.
// Thought: Coalesce has roslyn in it, so create this second executable on the fly with the Data project as a dependency.
// Thought: What if the data project already has an entry point? Can we just use that? How did the old EF tooling work when it used to require an entry point?


// Load Microsoft.EntityFrameworkCore.Abstractions.
// If we don't load our own, local copy,
// then DB context construction will fail because we will have loaded our own version of .Relational
// and therefore the version of .Relational and .Abstractions won't match.
typeof(IndexAttribute).GetTypeInfo();

// See efcore\src\ef\ReflectionOperationExecutor.cs
var dataAsmPath = (genContext.DataProject as RoslynProjectContext).MsBuildProjectContext.AssemblyFullPath;
var assembly = Assembly.LoadFrom(dataAsmPath);

var contextOperations = new Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations(
reporter: new LoggerReporter(Logger),
assembly: assembly,
startupAssembly: assembly,
projectDir: Environment.CurrentDirectory,
rootNamespace: null,
language: null,
nullable: false,
args: null);

using var context = contextOperations.CreateContext(dbContext.ClassViewModel.FullyQualifiedName);
dbContext.Model = context.Model;
// TODO: Use the Model during code gen.
}
}
finally
{
AppDomain.CurrentDomain.AssemblyResolve -= assemblyResolver;
}
#endif



var validationResult = ValidateContext.Validate(rr);
Expand Down Expand Up @@ -172,7 +243,9 @@ async Task<ProjectContext> TryLoadProject(ProjectConfiguration config)
{
try
{
var projectContext = ServiceProvider.GetRequiredService<IProjectContextFactory>().CreateContext(config, restorePackages);
var projectContext = ServiceProvider
.GetRequiredService<IProjectContextFactory>()
.CreateContext(config, restorePackages);

// Warn if the Coalesce versions referenced in the project don't
// match the version of the code generation being ran.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Core code generation library for IntelliTect.Coalesce</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net6.0;net7.0</TargetFrameworks>
<PlatformTarget>AnyCPU</PlatformTarget>
<OutputType>Library</OutputType>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Microsoft.Extensions.Logging;

namespace IntelliTect.Coalesce.CodeGeneration.Utilities
{
#if NET5_0_OR_GREATER
internal class LoggerReporter : Microsoft.EntityFrameworkCore.Design.Internal.IOperationReporter
{
public LoggerReporter(ILogger logger)
{
Logger = logger;
}

public ILogger Logger { get; }

public void WriteError(string message)
{
Logger.LogError(message);
}

public void WriteInformation(string message)
{
Logger.LogInformation(message);
}

public void WriteVerbose(string message)
{
Logger.LogTrace(message);
}

public void WriteWarning(string message)
{
Logger.LogWarning(message);
}
}
#endif
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Diagnostics;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -47,4 +48,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
.HasValue<AbstractImpl>("impl");
}
}

public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<AppDbContext>();
builder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=CoalesceTestDb;Trusted_Connection=True;");
return new AppDbContext(builder.Options);
}
}
}
47 changes: 47 additions & 0 deletions src/IntelliTect.Coalesce.Tests/Tests/ClonerTest.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
using IntelliTect.Coalesce.Helpers;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Xml.Linq;
using Xunit;

namespace IntelliTect.Coalesce.Tests
Expand Down Expand Up @@ -39,5 +45,46 @@ public class TestClass

public string Field;
}

#if NET5_0_OR_GREATER
class NullReporter : Microsoft.EntityFrameworkCore.Design.Internal.IOperationReporter
{
public void WriteError(string message)
{
}

public void WriteInformation(string message)
{
}

public void WriteVerbose(string message)
{
}

public void WriteWarning(string message)
{
}
}

[Fact]
public void AdHoc()
{
var contextType = "AppDbContext";
var assembly = Assembly.GetExecutingAssembly();
var _contextOperations = new Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations(
reporter: new NullReporter(),
assembly: assembly,
startupAssembly: assembly,
projectDir: Environment.CurrentDirectory,
rootNamespace: null,
language: null,
nullable: false,
args: null);

using var context = _contextOperations.CreateContext(contextType);
var model = context.Model;
var relationalModel = context.Model.GetRelationalModel();
}
#endif
}
}
10 changes: 8 additions & 2 deletions src/IntelliTect.Coalesce/IntelliTect.Coalesce.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,27 @@
</ItemGroup>

<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible($(TargetFramework), 'net5.0'))">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="[2.2,)" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="[2.2,)" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net5.0' ">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="[5.0,)" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="[5.0,)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="[5.0,)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[5.0,)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="[5.0,)" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net6.0' ">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="[6.0,)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="[6.0,)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[6.0,)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="[6.0,)" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net7.0' ">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="[7.0,)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="[7.0,)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="[7.0,)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="[7.0,)" />
</ItemGroup>
</Project>
3 changes: 3 additions & 0 deletions src/IntelliTect.Coalesce/TypeUsage/DbContextTypeUsage.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using IntelliTect.Coalesce.TypeDefinition;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using System;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -32,6 +33,8 @@ public DbContextTypeUsage(ClassViewModel classViewModel)

public IReadOnlyList<EntityTypeUsage> Entities { get; }

public IModel? Model { get; set; }

public override bool Equals(object? obj) => obj is DbContextTypeUsage that && that.ClassViewModel.Equals(ClassViewModel);

public override int GetHashCode() => ClassViewModel.GetHashCode();
Expand Down

0 comments on commit 132bd67

Please sign in to comment.