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

Other Framework Versions #27

Open
larsontim12 opened this issue Aug 20, 2020 · 6 comments
Open

Other Framework Versions #27

larsontim12 opened this issue Aug 20, 2020 · 6 comments

Comments

@larsontim12
Copy link

I was wondering if this would work with other .Net Frameworks? We currently have many applications that are in the 4.x framework and would like to incorporate something like this. Would the entire application need to be converted to .Net 4.x or does it incorporate features that are only available with .Net Core?

Can the framework load older Framework code in the .Net Core version?

Thanks,
Tim

@mikoskinen
Copy link
Contributor

Hey Tim,

.NET Framework isn't supported now, I'm afraid. 95% of the code is portable, but things related to assembly loading should be replaced with AppDomain-related code for the .NET Framework support.

I haven't tested loading .NET Framework DLL's into the .NET Core runtime. It could, perhaps, work. I'll try this one out.

@MrCircuit
Copy link

Hi Mikael,

What about .NET standard? We would like to use your library in a client side Blazor app.

Best regards,
Uwe

@MrCircuit
Copy link

Meanwhile, I tried to port the library myself and it may work. I would also suggest to add another plugin catalog type for web API sources. I'm working on a coarse prototype for this right now, based on your library. In case, you'll be interested in the outcome, drop a message.

@mikoskinen
Copy link
Contributor

Hi @MrCircuit,

Thanks for the message. The current idea is to move from .NET Core 3.1 to .NET 5, thus skipping .NET Standard. With .NET 5 we can target Blazor & most of the other platforms (though not .NET Framework).

Your prototype sound interesting, so when you have source code available, I'm interested in taking a peek :)

@MrCircuit
Copy link

You are right and it makes sense to skip it for the long run. Unfortunately, I'll need a temporary solution right now. I'll adapt your lib to this purpose on my own.

When the web catalog is running, I'll send it to you or create a pull request.

@MrCircuit
Copy link

Sorry for the following unconvenient way to send you the proposal, but I already had to adopt everything to be compatible to WebAssembly (main issue is no file system access) and it would break with your library at its current state. Nevertheless, the general approach is straightforward:

I took the FolderPluginCatalog as template and exchanged the folder search path with an API path together with a ready-to-use HttpClient instance (adopted constructor set). All folder search logic and assembly evaluation was cut away (because the API should deliver, what is needed and not more, but feel free to expand this as needed) and replaced by this code in the Initialize routine:

    public async Task Initialize()
    {
        // GET the list of files from the service URL and prepare them from the JSON response.
        var response = await _client.GetAsync(_apiPath);
        if (response.StatusCode != HttpStatusCode.OK)
        {
            return;
        }

        var json = await response.Content.ReadAsStringAsync();

        try
        {
            var pluginFileList = JsonSerializer.Deserialize<List<string>>(json);
            if (pluginFileList == null)
            {
                IsInitialized = true;
                return;
            }

            // GET each file in the list.
            foreach (var fileName in pluginFileList)
            {
                response = await _client.GetAsync(_apiPath + "/" + fileName);
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    continue;
                }

                var fileData = await response.Content.ReadAsByteArrayAsync();
                if (fileData == null)
                {
                    continue;
                }

                var assembly = Assembly.Load(fileData);
                if (assembly == null)
                {
                    continue;
                }

                // We are actually just delegating the responsibility from WebPluginCatalog to AssemblyPluginCatalog. 
                var assemblyCatalog = new AssemblyPluginCatalog(assembly);
                await assemblyCatalog.Initialize();

                _catalogs.Add(assemblyCatalog);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }

        IsInitialized = true;
    }

As you can see, a GET to the basic API route yields the plugin DLL list. After fetching it successfully, the files are fetched one by one by a GET on the specific file API route, loaded to an assembly and then added like it was done in FolderPluginCatalog to an AssemblyPluginCatalog.

Apart from this, the code from your specific AspNetCore library was adopted to load this catalog.

On the server side, a simple test controller did the remaining magic:

    private List<string> pluginFiles = new List<string>();

    public PluginController()
    {
        if (Directory.Exists("Plugins"))
        {
            var list = Directory.GetFiles("Plugins", "*.dll");
            pluginFiles.AddRange(list.Select(f => Path.GetFileNameWithoutExtension(f)));
        }
    }

    [HttpGet]
    public IActionResult GetPlugins()
    {
        return Ok(pluginFiles);
    }

    [HttpGet("{pluginName}")]
    public IActionResult GetPlugin(string pluginName)
    {
        if (!pluginFiles.Contains(pluginName))
        {
            return NotFound();
        }

        var filePath = Path.Combine("Plugins", pluginName + ".dll");

        using (var stream = System.IO.File.OpenRead(filePath))
        {
            var buf = new byte[stream.Length];
            stream.Read(buf, 0, buf.Length);

            var provider = new FileExtensionContentTypeProvider();
            string contentType;
            if (!provider.TryGetContentType(filePath, out contentType))
            {
                contentType = "application/octet-stream";
            }

            return new FileContentResult(buf, contentType);
        }
    }

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

3 participants