简体   繁体   中英

C#: use “Class Library” Project for plugins

this question follows my previous question . I have ac# ASP.NET application and i want to provide support for plugins. Plugins can be custom c# classes, javascript, html, css, images, etc.

I see no problem as long as my application is extended with c# classes because all the user has to do is create a new "class library" project in visual studio and implement the interfaces, i provide. Then build a dll out of it and upload it to my server. The plugin-developer can add static files (html, js, css, etc.) into this project as well but i found some problems with that:

  1. Every static file i add to the plugin project gets the build action "content" and it seems i cannot read those files from my server. (see my previously answered question ). I have to manually select "Embedded Resource" on each file, so it is packed with the plugin dll.
  2. I want to support Typescript for the plugins. The Typescript compiler generates javascript files in the same directory as the typescript-files. But the javascript files are not included in the project and therefore i have to include these in the plugin project and then set the correct build action. I don't want the plugin developers to do that all the time.
  3. If the static files have the build action "enbedded resources", then the server can pickup these files by using the assembly.GetManifestResourceNames() method. This method returns the resources as a string. The path is not separated by \\ or / but with a dot instead. So i am not able to distinguish between file path (this is relevant) or filename (also relevant to pickup the correct files), because the original filename can also have dots.

So i am starting to question the "class library" project type is right for my needs. Is there a way to get around of my issues or do i have to use another project type?

Thank you for any help!

Edit: Changed the question a little bit so it is better to understand.

You could make a zip package with the plugin dll and files. NuGet also uses this mechanism. A .nupkg is also just a zip file.

I would start by looking at MEF (Managed Extensibility Framework).

MSDN information can be found here: https://msdn.microsoft.com/en-us/library/dd460648(v=vs.110).aspx

From that link you can get more information and I believe there is a tutorial as well.

Oh, for me it seems very simple.

  1. Let the developer create the plugin freestyle and put all the additional files in a directory, let's call it extras
  2. To implement the your interface they will need your assembly so I guess you will ship it via nuget, or just some link. No matter what the case, provide them with some powershell script what will be required to run before the final build
  3. The script would create zip archive from the extras directory and add it to the ClassLibrary project as EmbeddedResource.
  4. As you mentioned earlier, you can access EmbeddedResource. So all you would do is to unpack it and you would have the exact directory tree.

The best idea would be to provide project template with script included, and also the empty zip archive added as embedded resource (it will be easier to just pack the files in the script and replace the file), and pre-build action set to run the script.

Am I missing something?

What about this.

In your web application, you could add a function that loop into your plugin directory and find DLL implementing an Iplugin (name is up to you) interface.

The interface is defined in a class library that both your web application and plugins have to implement.

You can use the Httpcontext Server mappath to read javascript and other files.

Here is a very basic implementation

First, you have the plugin interface (a class library implemented both by the web application and the individual plugins) I implemented sample properties and methods...

using System.Web;

public interface IPlugin
{
string Name { get; set; }
string Output { get; set; }
void Load(ref httpcontext Context);
void Dispose();
void Display();
}

Next, you have the Actual plugin class library we want to implement.

using System.Web;
using IPlugins;

public class AwesomePlugin : IPlugins.IPlugin
{

private string _Name = "AwesomePlugin";

private HttpContext _Context;
public string Name {
    get { return _Name; }
    set { _Name = value; }
}

public string Output {
    get { return "Yay !!!"; }
    set {
        throw new NotImplementedException();
    }
}

public void Display()
{
    throw new NotImplementedException();
}

public void Dispose()
{
    throw new NotImplementedException();
}


public void Load(ref Web.HttpContext Context)
{


}
}

Finally, you dynamically load your plugins so you can use them in your application.

private Dictionary<string, IPlugins.IPlugin> _Plugins = new     Dictionary<string, IPlugins.IPlugin>();
public void LoadPlugins()
{
lock (static_LoadPlugins_IpluginType_Init) {
    try {
        if (InitStaticVariableHelper(static_LoadPlugins_IpluginType_Init)) {
            static_LoadPlugins_IpluginType = typeof(IPlugins.IPlugin);
        }
    } finally {
        static_LoadPlugins_IpluginType_Init.State = 1;
    }
}
string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";
dynamic Plugins = io.Directory.GetFiles(ServerPath);


foreach (string PluginPath in Plugins) {
    dynamic Assembly = system.Reflection.Assembly.LoadFile(PluginPath);

    Type PluginClass = Assembly.GetTypes.Where(T => T.GetInterface("IPlugin") != null).First;
    IPlugins.IPlugin MyPlugin = Activator.CreateInstance(PluginClass);
    MyPlugin.Load(httpcontext.Current);
    _Plugins.@add(PluginClass.ToString, MyPlugin);


}

}
static bool     InitStaticVariableHelper(Microsoft.VisualBasic.CompilerServices.StaticLocalInitFlag flag)
{
if (flag.State == 0) {
    flag.State = 2;
    return true;
} else if (flag.State == 2) {
    throw new Microsoft.VisualBasic.CompilerServices.IncompleteInitialization();
} else {
    return false;
}
}

That way, you can implement whatever you want in your plugin. I believe you could load your plugins in a separate appdomain with restricted permissions to everything.

The files (Javascript / CSS / Html) should be available by accessing the full path of the file.

string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";

If the resources is embedded into the plugin DLL, you could read the stream from the loaded assembly or let the plugin manage its own embedded files.

For question Number 2, you can use

MS Build

to change the contenttype during build process. You have to make yourself confident with MS Build

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM