Skip to content

Instantly share code, notes, and snippets.

@JaapM
Created January 11, 2016 10:50
Embed
What would you like to do?
DotNet variables Assembly Resolver for NAV 2016
OBJECT Codeunit 50000 Assembly Resolver
{
OBJECT-PROPERTIES
{
Date=24-12-15;
Time=09:03:26;
Modified=Yes;
Version List=;
}
PROPERTIES
{
OnRun=VAR
Asm@1010 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.Assembly";
AsmClient@1000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.Assembly" RUNONCLIENT;
Object@1001 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object";
ObjectClient@1100498002 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object" RUNONCLIENT;
IsLoaded@1100498003 : Boolean;
NetEnv@1100498001 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Environment";
NetEnvClient@1100498004 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Environment" RUNONCLIENT;
BEGIN
IF NOT IsServerSideLoaded THEN BEGIN
IF RetrieveResolverAssembly(Asm) THEN BEGIN
Object := Asm.GetType('Myapp.Nav.AssemblyResolver.Resolver').GetProperty('SingleInstanceObject').GetValue(0);
AddAssembliesNst(Object);
END;
END;
IF IsWindowsClient() THEN BEGIN
IF NOT IsClientSideLoaded THEN BEGIN
IF RetrieveResolverAssembly(AsmClient) THEN BEGIN
ObjectClient := AsmClient.GetType('Myapp.Nav.AssemblyResolver.Resolver').GetProperty('SingleInstanceObject').GetValue(0);
AddAssembliesRtc(ObjectClient);
END;
END;
END;
END;
}
CODE
{
[TryFunction]
LOCAL PROCEDURE IsServerSideLoaded@1100498001();
VAR
Tester@1100498000 : DotNet "'Myapp.Nav.AssemblyResolver, Version=9.0.0.0, Culture=neutral, PublicKeyToken=58e0876c5da5c121'.Myapp.Nav.AssemblyResolver.Tester";
BEGIN
//If assembly with the resolver is not yet loaded, this call will fail
Tester := Tester.Tester;
END;
[TryFunction]
LOCAL PROCEDURE IsClientSideLoaded@1100498006();
VAR
Tester@1100498000 : DotNet "'Myapp.Nav.AssemblyResolver, Version=9.0.0.0, Culture=neutral, PublicKeyToken=58e0876c5da5c121'.Myapp.Nav.AssemblyResolver.Tester" RUNONCLIENT;
BEGIN
//If assembly with the resolver is not yet loaded, this call will fail
Tester := Tester.Tester;
//This will also succeed when assemblies are in the add-ins folder of the development environment, which is fine
END;
LOCAL PROCEDURE AddAssembliesNst@6(Resolver@1100498000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object");
VAR
Asmbl@1000 : Record 11096450;
BEGIN
WITH Asmbl DO BEGIN
SETFILTER(Tier,'%1|%2',Tier::Both,Tier::Server);
IF FINDSET() THEN
REPEAT
AddAssembly(Asmbl,Resolver,Tier::Server);
UNTIL NEXT = 0;
END;
END;
LOCAL PROCEDURE AddAssembliesRtc@1100498000(Resolver@1100498000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object");
VAR
Asmbl@1000 : Record 11096450;
BEGIN
WITH Asmbl DO BEGIN
SETFILTER(Tier,'%1|%2',Tier::Both,Tier::Client);
IF FINDSET() THEN
REPEAT
AddAssembly(Asmbl,Resolver,Tier::Client);
UNTIL NEXT = 0;
END;
END;
LOCAL PROCEDURE AddAssembly@7(Asmbl@1000 : Record 11096450;VAR Resolver@1007 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object";TargetTier@1003 : Integer);
VAR
Asm@1004 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.Assembly";
Bytes@1008 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array";
MemStr@1002 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream";
MethodInfo@1005 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.MethodInfo";
MethodInfoClient@1010 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.MethodInfo" RUNONCLIENT;
Params@1006 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array";
ParamsClient@1009 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array" RUNONCLIENT;
Object@1011 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Object";
InStr@1001 : InStream;
BEGIN
LoadAssemblyBytes(Asmbl,Bytes);
WITH Asmbl DO BEGIN
CASE TargetTier OF
Tier::Server:
BEGIN
MethodInfo := Resolver.GetType().GetMethod('AddAssembly');
Params := Params.CreateInstance(GETDOTNETTYPE(Object),2);
Params.SetValue("Assembly Name",0);
Params.SetValue(Bytes,1);
MethodInfo.Invoke(Resolver,Params);
END;
Tier::Client:
BEGIN
IF NOT IsWindowsClient() THEN
EXIT;
MethodInfoClient := Resolver.GetType().GetMethod('AddAssembly');
ParamsClient := ParamsClient.CreateInstance(GETDOTNETTYPE(Object),2);
ParamsClient.SetValue("Assembly Name",0);
ParamsClient.SetValue(Bytes,1);
MethodInfoClient.Invoke(Resolver,ParamsClient);
END;
END;
END;
END;
LOCAL PROCEDURE IsWindowsClient@4() : Boolean;
BEGIN
EXIT(CURRENTCLIENTTYPE = CLIENTTYPE::Windows);
END;
LOCAL PROCEDURE LoadAssemblyBytes@65(Asmbl@1000 : Record 11096450;VAR Bytes@1001 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array");
VAR
MemStr@1004 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream";
InStr@1003 : InStream;
Byte@1002 : Byte;
BEGIN
WITH Asmbl DO BEGIN
CALCFIELDS(Assembly);
Assembly.CREATEINSTREAM(InStr);
MemStr := MemStr.MemoryStream();
COPYSTREAM(MemStr,InStr);
Bytes := MemStr.ToArray();
END;
END;
LOCAL PROCEDURE RetrieveResolverAssembly@60(VAR Asm@1000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Reflection.Assembly") : Boolean;
VAR
Asmbl@1001 : Record 11096450;
Bytes@1100498000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array";
BEGIN
Asmbl.SETRANGE("Assembly Name" , 'Myapp.Nav.AssemblyResolver, Version=9.0.0.0, Culture=neutral, PublicKeyToken=58e0876c5da5c121');
IF Asmbl.FINDFIRST THEN BEGIN
LoadAssemblyBytes(Asmbl,Bytes);
Asm := Asm.Load(Bytes);
EXIT(TRUE);
END;
EXIT(FALSE);
END;
BEGIN
{
}
END.
}
}
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Myapp.Nav.AssemblyResolver
{
public class Resolver
{
public static Resolver SingleInstance { get; private set; } = new Resolver();
public static Object SingleInstanceObject { get { return SingleInstance; } }
private readonly Dictionary<string, byte[]> _assemblies = new Dictionary<string, byte[]>();
public void AddAssembly(string name, byte[] asm)
{
if (_assemblies.ContainsKey(name))
_assemblies.Remove(name);
_assemblies.Add(name, asm);
}
public bool IsLoaded { get { return _assemblies.Count > 0; } }
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return _assemblies.ContainsKey(args.Name) ? Assembly.Load(_assemblies[args.Name]) : null;
}
private Resolver()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
}
/// <summary>
/// Dummy class used in NAV to test if assembly is loaded
/// </summary>
public class Tester
{
}
}
@mitrov
Copy link

mitrov commented Jan 15, 2016

Hi JaapM

Thanks for this gist! When I copy your c# to VS2010 or VS2012 I get:
image

Are you working in VS2015?

Update: Yep... this doesn't happen in VS2015

@mitrov
Copy link

mitrov commented Jan 22, 2016

HI JaapM

How do you solve the compile time problem? My understanding is that the actual DLL has to be at least in Add-ins folder of the client or server. For instance when I import a FOB in NAV2015 and the DLL is not there yet... the NAV object is not compiled and cannot be used until I compile it.

@vjekob
Copy link

vjekob commented Jan 25, 2016

mitrov, see here for how it works without being anywhere in the add-ins:
http://vjeko.com/sorting-out-the-dll-hell-part-3-the-code

I am actually working on a fix for the memory leak issues and I'll post an updated to my solution today or tomorrow.

@mitrov
Copy link

mitrov commented Jan 28, 2016

Thanks Vjeko!

If I understand your solution correctly you solve the compile time problem with the function CompileResolverAssembly for your ResolverAssembly.
But for my assemblies I would need to do the same thing... minifying my assembly c# code and write a CompileMyAssembly function... correct?
And because I'm using 3rd party assemblies as well... this is not really an option for me. So I have to deploy my assemblies including 3rd party assemblies at least once to the addins-folder to make sure that the nav objects compile on the customers side. .. right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment