Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assembly.Get does not work in single file executable deployments #87

Open
aliozgur opened this issue Nov 29, 2024 · 2 comments
Open

Assembly.Get does not work in single file executable deployments #87

aliozgur opened this issue Nov 29, 2024 · 2 comments

Comments

@aliozgur
Copy link

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}");
            }

        }

    }
@PatrickRitchie
Copy link
Contributor

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
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants