MTConnect.NET icon indicating copy to clipboard operation
MTConnect.NET copied to clipboard

Assembly.Get does not work in single file executable deployments

Open aliozgur opened this issue 1 year ago • 2 comments

Hi,

We are using MTConnect.NET 6.5.0. We deploy our application as a single file executable. Given this context Assmbly.Get() method found in MTConnect.NET uses Assebly.Location to find all dll files. But, in single file dployments Assembly.Location returns empty and actually all dll files are compressed and embedded inside the single file executable.

Current workaround for us was to create a static class and add document formatters to ResponseDocumentFormatter private static fields using reflection.

public static class MtConnectRuntime
    {
        public static void Initialize()
        {
            SetMtConnectResponseDocumentFormatters();
        }

        private static void SetMtConnectResponseDocumentFormatters()
        {
            // [AÖ] This is needed beacuse when deployment is selected SingleFile Assemblies.Get() (found in MTConnect.NET lib) can not inspect assemblies
            // See: https://learn.microsoft.com/en-us/dotnet/core/deploying/single-file/warnings/il3000
            // See: https://learn.microsoft.com/en-us/dotnet/core/deploying/single-file/overview?tabs=cli
            try
            {
                var fields = typeof(ResponseDocumentFormatter).GetFields(BindingFlags.NonPublic | BindingFlags.Static);

                var _firstRead = fields.FirstOrDefault(x => x.Name == "_firstRead");
                var _formatters = fields.FirstOrDefault(x => x.Name == "_formatters");
                if (_firstRead != null)
                {
                    _firstRead.SetValue(null, false);
                }

                if (_formatters != null)
                {

                    var formatters = (ConcurrentDictionary<string, IResponseDocumentFormatter>)_formatters.GetValue(null);
                    formatters.Clear();

                    var xml = new XmlResponseDocumentFormatter();
                    formatters.TryAdd(xml.Id.ToLower(), xml);

                    var json = new JsonResponseDocumentFormatter();
                    formatters.TryAdd(json.Id.ToLower(), json);

                    var html = new JsonHttpResponseDocumentFormatter();
                    formatters.TryAdd(html.Id.ToLower(), html);

                    var mqtt = new JsonMqttResponseDocumentFormatter();
                    formatters.TryAdd(mqtt.Id.ToLower(), mqtt);
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine($"{nameof(MtConnectRuntime.SetMtConnectResponseDocumentFormatters)} {ex.ToErrorMessage()}\r\n{ex.StackTrace}");
            }

        }

    }

aliozgur avatar Nov 29 '24 15:11 aliozgur

Thanks for the information! Glad to hear you have a workaround and I'll see if there is a good way to toggle how the formatters are loaded.

PatrickRitchie avatar Dec 20 '24 22:12 PatrickRitchie

It looks like using the below arguments with the DotNet CLI publish command seems to work:

dotnet publish -c:Release -f:net8.0 -r:win-x64 -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true --self-contained false

The IncludeAllContentForSelfExtract option seems to be what allows the combined assemblies to be read as if they were separate.

Thanks for bringing this up! I'm planning to build future versions using this.

PatrickRitchie avatar Jan 18 '25 17:01 PatrickRitchie