Skip to content

Commit 8498287

Browse files
hazzikTrifleplfredericDelaporte
authored
Add ability to load types from in-memory-only assemblies
GetType only works on assemblies loaded from disk. To overcome this limitation we need to supply an assembly resolver. Co-Authored-by: Trifle.pl <[email protected]> Co-Authored-by: Frédéric Delaporte <[email protected]>
1 parent 5c3033a commit 8498287

File tree

4 files changed

+310
-1
lines changed

4 files changed

+310
-1
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System;
12+
using NUnit.Framework;
13+
using System.CodeDom.Compiler;
14+
using System.Linq;
15+
using Microsoft.CSharp;
16+
using System.Reflection;
17+
using NHibernate.Engine;
18+
using NHibernate.Mapping.ByCode;
19+
using NHibernate.Tool.hbm2ddl;
20+
21+
namespace NHibernate.Test.CfgTest
22+
{
23+
using System.Threading.Tasks;
24+
[TestFixture]
25+
public class LoadMemoryOnlyAssembyTestAsync
26+
{
27+
[Test]
28+
public async Task CompileMappingForDynamicInMemoryAssemblyAsync()
29+
{
30+
const int assemblyGenerateCount = 10;
31+
var code = GenerateCode(assemblyGenerateCount);
32+
var assembly = CompileAssembly(code);
33+
34+
var config = TestConfigurationHelper.GetDefaultConfiguration();
35+
var mapper = new ModelMapper();
36+
mapper.AddMappings(assembly.GetExportedTypes());
37+
config.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
38+
39+
Assert.That(config.ClassMappings, Has.Count.EqualTo(assemblyGenerateCount), "Not all virtual assembly mapped");
40+
41+
// Ascertain the mappings are usable.
42+
using (var sf = config.BuildSessionFactory())
43+
{
44+
var se = new SchemaExport(config);
45+
await (se.CreateAsync(false, true));
46+
try
47+
{
48+
using (var s = sf.OpenSession())
49+
using (var tx = s.BeginTransaction())
50+
{
51+
foreach (var entityClass in assembly.GetExportedTypes()
52+
.Where(t => !typeof(IConformistHoldersProvider).IsAssignableFrom(t)))
53+
{
54+
var ctor = entityClass.GetConstructor(Array.Empty<System.Type>());
55+
Assert.That(ctor, Is.Not.Null, $"Default constructor for {entityClass} not found");
56+
var entity = ctor.Invoke(Array.Empty<object>());
57+
await (s.SaveAsync(entity));
58+
}
59+
60+
await (tx.CommitAsync());
61+
}
62+
63+
using (var s = sf.OpenSession())
64+
using (var tx = s.BeginTransaction())
65+
{
66+
var entities = await (s.CreateQuery("from System.Object").ListAsync<object>());
67+
Assert.That(entities, Has.Count.EqualTo(assemblyGenerateCount), "Not all expected entities were persisted");
68+
69+
await (tx.CommitAsync());
70+
}
71+
}
72+
finally
73+
{
74+
TestCase.DropSchema(false, se, (ISessionFactoryImplementor) sf);
75+
}
76+
}
77+
}
78+
79+
private static string[] GenerateCode(int assemblyCount)
80+
{
81+
const string codeTemplate = @"
82+
using NHibernate.Mapping.ByCode.Conformist;
83+
84+
public class Entity$$INDEX$$
85+
{
86+
public virtual int ID { get; set; }
87+
public virtual string Name { get; set; }
88+
}
89+
90+
public class Entity$$INDEX$$Map : ClassMapping<Entity$$INDEX$$>
91+
{
92+
public Entity$$INDEX$$Map ()
93+
{
94+
Table(""Entity$$INDEX$$"");
95+
96+
Id(x => x.ID, m =>
97+
{
98+
m.Column(""`ID`"");
99+
});
100+
101+
Property(x => x.Name, m =>
102+
{
103+
m.Column(""`Name`"");
104+
});
105+
}
106+
}";
107+
var code = new string[assemblyCount];
108+
for (var i = 0; i < code.Length; i++)
109+
code[i] = codeTemplate.Replace("$$INDEX$$", i.ToString("000"));
110+
return code;
111+
}
112+
113+
private static Assembly CompileAssembly(string[] code)
114+
{
115+
var parameters = new CompilerParameters
116+
{
117+
GenerateInMemory = true,
118+
TreatWarningsAsErrors = false,
119+
GenerateExecutable = false,
120+
CompilerOptions = "/optimize"
121+
};
122+
parameters.ReferencedAssemblies.AddRange(
123+
new[]
124+
{
125+
"System.dll",
126+
"System.Core.dll",
127+
"System.Data.dll",
128+
new Uri(typeof(Cfg.Environment).Assembly.EscapedCodeBase).LocalPath
129+
});
130+
131+
try
132+
{
133+
using (var provider = new CSharpCodeProvider())
134+
{
135+
var compile = provider.CompileAssemblyFromSource(parameters, code);
136+
137+
if (compile.Errors.HasErrors)
138+
{
139+
var text = "Compile error: " +
140+
string.Join(System.Environment.NewLine, compile.Errors.Cast<CompilerError>());
141+
142+
Assert.Fail(text);
143+
}
144+
145+
return compile.CompiledAssembly;
146+
}
147+
}
148+
catch (PlatformNotSupportedException e)
149+
{
150+
Assert.Inconclusive(e.Message);
151+
return null;
152+
}
153+
}
154+
}
155+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
using System;
2+
using NUnit.Framework;
3+
using System.CodeDom.Compiler;
4+
using System.Linq;
5+
using Microsoft.CSharp;
6+
using System.Reflection;
7+
using NHibernate.Engine;
8+
using NHibernate.Mapping.ByCode;
9+
using NHibernate.Tool.hbm2ddl;
10+
11+
namespace NHibernate.Test.CfgTest
12+
{
13+
[TestFixture]
14+
public class LoadMemoryOnlyAssembyTest
15+
{
16+
[Test]
17+
public void CompileMappingForDynamicInMemoryAssembly()
18+
{
19+
const int assemblyGenerateCount = 10;
20+
var code = GenerateCode(assemblyGenerateCount);
21+
var assembly = CompileAssembly(code);
22+
23+
var config = TestConfigurationHelper.GetDefaultConfiguration();
24+
var mapper = new ModelMapper();
25+
mapper.AddMappings(assembly.GetExportedTypes());
26+
config.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
27+
28+
Assert.That(config.ClassMappings, Has.Count.EqualTo(assemblyGenerateCount), "Not all virtual assembly mapped");
29+
30+
// Ascertain the mappings are usable.
31+
using (var sf = config.BuildSessionFactory())
32+
{
33+
var se = new SchemaExport(config);
34+
se.Create(false, true);
35+
try
36+
{
37+
using (var s = sf.OpenSession())
38+
using (var tx = s.BeginTransaction())
39+
{
40+
foreach (var entityClass in assembly.GetExportedTypes()
41+
.Where(t => !typeof(IConformistHoldersProvider).IsAssignableFrom(t)))
42+
{
43+
var ctor = entityClass.GetConstructor(Array.Empty<System.Type>());
44+
Assert.That(ctor, Is.Not.Null, $"Default constructor for {entityClass} not found");
45+
var entity = ctor.Invoke(Array.Empty<object>());
46+
s.Save(entity);
47+
}
48+
49+
tx.Commit();
50+
}
51+
52+
using (var s = sf.OpenSession())
53+
using (var tx = s.BeginTransaction())
54+
{
55+
var entities = s.CreateQuery("from System.Object").List<object>();
56+
Assert.That(entities, Has.Count.EqualTo(assemblyGenerateCount), "Not all expected entities were persisted");
57+
58+
tx.Commit();
59+
}
60+
}
61+
finally
62+
{
63+
TestCase.DropSchema(false, se, (ISessionFactoryImplementor) sf);
64+
}
65+
}
66+
}
67+
68+
private static string[] GenerateCode(int assemblyCount)
69+
{
70+
const string codeTemplate = @"
71+
using NHibernate.Mapping.ByCode.Conformist;
72+
73+
public class Entity$$INDEX$$
74+
{
75+
public virtual int ID { get; set; }
76+
public virtual string Name { get; set; }
77+
}
78+
79+
public class Entity$$INDEX$$Map : ClassMapping<Entity$$INDEX$$>
80+
{
81+
public Entity$$INDEX$$Map ()
82+
{
83+
Table(""Entity$$INDEX$$"");
84+
85+
Id(x => x.ID, m =>
86+
{
87+
m.Column(""`ID`"");
88+
});
89+
90+
Property(x => x.Name, m =>
91+
{
92+
m.Column(""`Name`"");
93+
});
94+
}
95+
}";
96+
var code = new string[assemblyCount];
97+
for (var i = 0; i < code.Length; i++)
98+
code[i] = codeTemplate.Replace("$$INDEX$$", i.ToString("000"));
99+
return code;
100+
}
101+
102+
private static Assembly CompileAssembly(string[] code)
103+
{
104+
var parameters = new CompilerParameters
105+
{
106+
GenerateInMemory = true,
107+
TreatWarningsAsErrors = false,
108+
GenerateExecutable = false,
109+
CompilerOptions = "/optimize"
110+
};
111+
parameters.ReferencedAssemblies.AddRange(
112+
new[]
113+
{
114+
"System.dll",
115+
"System.Core.dll",
116+
"System.Data.dll",
117+
new Uri(typeof(Cfg.Environment).Assembly.EscapedCodeBase).LocalPath
118+
});
119+
120+
try
121+
{
122+
using (var provider = new CSharpCodeProvider())
123+
{
124+
var compile = provider.CompileAssemblyFromSource(parameters, code);
125+
126+
if (compile.Errors.HasErrors)
127+
{
128+
var text = "Compile error: " +
129+
string.Join(System.Environment.NewLine, compile.Errors.Cast<CompilerError>());
130+
131+
Assert.Fail(text);
132+
}
133+
134+
return compile.CompiledAssembly;
135+
}
136+
}
137+
catch (PlatformNotSupportedException e)
138+
{
139+
Assert.Inconclusive(e.Message);
140+
return null;
141+
}
142+
}
143+
}
144+
}

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
<PackageReference Include="MySql.Data" Version="6.9.11" />
6969
</ItemGroup>
7070
<ItemGroup Condition="'$(TargetFramework)'=='netcoreapp2.0'">
71+
<PackageReference Include="System.CodeDom" Version="4.4.0" />
7172
<PackageReference Include="System.Data.SqlClient" Version="4.4.2" />
7273
<PackageReference Include="System.Data.OracleClient" Version="1.0.8" />
7374
<PackageReference Include="System.Data.Odbc" Version="4.5.0-preview1-25914-04" />

src/NHibernate/Util/ReflectHelper.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,6 @@ public static System.Type TypeFromAssembly(AssemblyQualifiedTypeName name, bool
398398
{
399399
// Try to get the type from an already loaded assembly
400400
System.Type type = System.Type.GetType(name.ToString());
401-
402401
if (type != null)
403402
{
404403
return type;
@@ -413,6 +412,16 @@ public static System.Type TypeFromAssembly(AssemblyQualifiedTypeName name, bool
413412
return null;
414413
}
415414

415+
//Load type from already loaded assembly
416+
type = System.Type.GetType(
417+
name.ToString(),
418+
an => AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == an.FullName),
419+
null);
420+
if (type != null)
421+
{
422+
return type;
423+
}
424+
416425
Assembly assembly = Assembly.Load(name.Assembly);
417426

418427
if (assembly == null)

0 commit comments

Comments
 (0)