0
votes

Situation:

  1. I merged 2 assemblies: Functionality.dll and Models.dll
  2. Functionality calls: RazorEngine.Razor.Parse("template", new Models.Model()) (simplified) (Functionality.dll references Models.dll and RazorEngine. RazorEngine is added to the GAC. Merged Assembly not for various reasons)
  3. Merged assembly is uploaded into 3th party tool. This 3th party tool loads this merged dll dynamically into its appdomain and executes it.

Result: Exception is thrown: Unable to compile template. The type or namespace name 'Models' could not be found. (are you missing an assembly reference?)

To me this is weird, because the 'Models' namespace is part of the merged assembly. And if I check (with the debugger) it is loaded. (AppDomain.CurrentDomain.GetAssemblies). BUT: the merged assembly has no location. (Assembly.Location = ""). I assume this is because it was dynamically loaded by the 3th party application? And that causes the RazorEngine to throw this exception, because it adds references to all loaded assemblies by location (@params.ReferencedAssemblies.Add(locationofdll))'

My question is:

How can I make sure that my dynamically loaded, merged dll is (correctly) loaded into the appdomain so the RazorEngine can add a reference to it? For me it's not handy to ALSO put my merged dll into the GAC. (I already uploaded it into my 3th party application). And that 3th party application has potentially multiple servers. I know, it is possible to add it in the GAC, and maybe that's what the GAC is for, but I was just wondering if I could somehow load my merged assembly (again?) into the currently running AppDomain (With a location?) so the RazorEngine can add a reference to it. (Weird right? The RazorEngine.dll runs in the same AppDomain as the merged dll. I doublechecked that)

Long question. Sorry for that. I hope someone can give me some pointers on how this all hangs together (AppDomain, dynamically loaded, merged assemblies using a GAC assembly (RazorEngine) that needs to know the Types in the merged assembly.... pfft)

Thanks!

1
have you tried fuslogvw?leppie
Yes, but to no avail. It didn't 'catch' the loading of the assemblies :(Albert Romkes

1 Answers

0
votes

This is (as you already noticed) because RazorEngine ignores all assemblies with a null Location. While I cannot give you an exact answer I can probably give you some hints/workarounds (on the RazorEngine specific part of the question).

There is not much you can do in old versions (<3.5.0):

Basically you somehow need to load the RazorEngine code as an assembly with a not null Location property (I dont know if there are ways to do that in your situation). Have you actually tried to load the assembly again via Assembly.LoadFrom method? If that doesn't work you can maybe get away with loading the Assembly properly in a new AppDomain and do the compilation there. As soon as the reference is loaded with an not-null Location property RazorEngine should pick it up...

On newer versions (wait for the next release (>=3.5.0) or build yourself from develop branch): you can possibly work-around that situation with the IReferenceResolver interface.

  • You know where your Assembly is and can add that path manually to the returned collection (only use the From(string) overload):

    class MyIReferenceResolver : IReferenceResolver {
        public IEnumerable<CompilerReference> GetReferences(TypeContext context, IEnumerable<CompilerReference> includeAssemblies) {
            // add all references needed by the compilation of the template
            // You should include the includeAssemlies as well :)
            return new [] { 
                CompilerReference.From("Path-to-my-custom-assembly"), // file path (string)
                CompilerReference.From(typeof(MyType).Assembly), // Assembly (roslyn only in your situation)
                CompilerReference.From(assemblyInByteArray), // byte array (roslyn only)
                CompilerReference.From(File.OpenRead(assembly)), // stream (roslyn only)
            };
        }
    }
    

    Then set it in the configuration

    config.ReferenceResolver = new MyIReferenceResolver();
    

    Docs are here.

  • If you do not know the location of your assembly: Try to use the Roslyn compiler and a CompilerReference.From(typeof(MyType).Assembly) reference. Docs are here and here.

Source: I currently contributed the new API to the RazorEngine project.

Note: The exact interface may change until we do a proper release.