I am building an application and this is my first experience with extensibility, plugins or addons, so I'm really new to this.
Using a tutorial from here I've built what I believe is a suitable interface and importer (both below), but I am unsure on how exactly to build the plugins themselves, as building them as pages in separate solutions will build them into a separate application, not provide the separate compiled page files.
Can someone either provide me with a basic example of a WPF application with external addon Pages or provide me with a link to a suitable source? I have had absolutely no luck finding anything like this.
Interface:
internal interface PORTaddon
{
public string Name { get; }
public Page PageReference { get; }
void Setup();
}
Importer:
if (!Directory.Exists(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Addons")))
{
Directory.CreateDirectory(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Addons"));
}
//Get Addons path & contents
string addonPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Addons");
DirectoryCatalog catalog = new DirectoryCatalog(addonPath);
CompositionContainer container = new CompositionContainer(catalog);
//Addons importer
IEnumerable<PORTaddon> addons = container.GetExportedValues<PORTaddon>();
Related
I have a separate Module in a C# class library project which is loaded via MEF Imports.
As an initial attempt at bringing sitemap information along with the Module I have added a web.sitemap file with the necessary mark-up but I can't seem to get a clear sense of how to load it and attach it to the Host MVC project sitemap in memory.
I also tried to use an MvcSiteMapNode attribute but haven't really been able to get this working yet.
Firstly, which is the easiest method to use, Attribute or SiteMap?
Secondly can anyone point me to guidance on how to do either of these please?
I have a preference for using the sitemap file because it should avoid dependencies on MvcSiteMapProvider in the MEF module.
You could embed XML into your modules and then somehow export them through MEF, I am pretty sure that is an option.
To load the XML from memory, you will need to use an external DI container and implement the IXmlSource interface yourself.
public class MyXmlSource : IXmlSource
{
public XDocument GetXml()
{
// Load the XML from wherever...
}
}
Then the DI configuration would just need to swap out the default XmlSource.
var excludeTypes = new Type[] {
typeof(IXmlSource)
};
// More DI config not shown...
this.For<IXmlSource>()
.Use<MyXmlSource>();
Of course, if you have multiple files, you would need multiple registrations of IXmlSource and XmlSiteMapNodeProvider. The XmlSiteMapNodeProvider only has the ability to merge the nodes below the root node, so you can't just place them anywhere in the SiteMap.
XML and Attributes are the least flexible options for configuring MvcSiteMapProvider.
Another Approach
The most flexible option is to use ISiteMapNodeProvider implementations to load your configuration data which also requires an external DI container. The second-best option is to use dynamic node provider implementations, but the limitation there is that they cannot contain the root node and require either an XML node or .NET attribute to host the provider on.
However, if you don't want any 3rd party dependencies, you will need a custom abstraction (DTO) that is defined in your own base library that is exported via MEF, and then used by either ISiteMapNodeProvider or IDynamicNodeProvider to load the data from the abstraction.
public class SiteMapNodeDto
{
public string Key { get; set; }
public string ParentKey { get; set; }
public string Title { get; set; }
public IDictionary<string, object> RouteValues { get; set; }
// Additional properties...
}
And then have an interface of some kind that your module can implement to provide nodes.
public interface IModuleSiteMapNodeProvider
{
IEnumerable<SiteMapNodeDto> GetNodes();
}
My brief experience with MEF was several years ago, so I don't recall how you export a type and import it somewhere else, so you will need to work that out on your own.
Then in your main application, you could create a method for converting from your DTO to the type that is expected by MvcSiteMapProvider (ISiteMapNodeToParentRelation or DynamicNode).
public static SiteMapNodeProviderExtensions
{
public static ISiteMapToParentRelation CreateNode(this ISiteMapNodeHelper helper, SiteMapNodeDto dto, string sourceName)
{
string key = helper.CreateNodeKey(
dto.ParentKey,
dto.Key,
dto.Url,
dto.Title,
dto.Area,
dto.Controller,
dto.Action,
dto.HttpMethod,
dto.Clickable);
var nodeParentMap = helper.CreateNode(key, attribute.ParentKey, sourceName);
var node = nodeParentMap.Node;
node.Title = title;
// Populate remaining properties...
return nodeParentMap;
}
}
And then in the ISiteMapNodeProvider you would simply need to call this method for each node.
public IEnumerable<ISiteMapNodeToParentRelation> GetSiteMapNodes(ISiteMapNodeHelper helper)
{
string sourceName = typeof(SiteMapNodeDto).Name;
IEnumerable<SiteMapNodeDto> dtos = someExternalSource.GetNodes();
foreach (var dto in dtos)
{
yield return helper.CreateNode(dto, sourceName);
}
}
If you want to make providing SiteMap nodes a first class feature for your module developers (and make it really easy to understand the hierarchy of nodes like you can with XML), you could create a Fluent API so you can express the nodes in code. Have a look at this pull request for one such approach.
We are implementing a plug and play module for our application where user can load and unload the desired class library at runtime. So I have decided to use MEF and shadow copying of class libraries.
The thing here is each class library may have different configuration properties which needs to set by user. My main application has no knowledge about the configurations present in the class library.
Now the problem is when I try to transfer the application configuration file loaded with class library from one application domain to another.
Without MEF, I have just returned Settings.Default from the class library and I have used it in our main application to edit the settings. With MEF and shadow copying, It doesn't seems to be working because
The object type needs to known to both sides.
I cannot implement MarshalByRefObject on the settings file since
the settings file is already extending ApplicationSettingsBase which
is an abstract class and c# doesn't supports multiple inheritance.
Currently I am creating a class which holds all the properties as string and creating a GUI in my main application based on this class content.
public class ExtensionModuleConfiguration : MarshalByRefObject
{
public string Name { get; set; }
public string Value { get; set; }
public List<string> Options { get; set; }
public UIElements ToolUIElement { get; set; }
}
public enum UIElements
{
ComboBox,
TextBox
}
I must say this is not the best solution.
Can someone suggest a better way to set the configurations of a class library in MEF?
There two ways how you can do it. You must inform .NET which app.config should be loaded in the appdomain of your MEF plugin class.
Therefore you can either point particular app.config for your plugin DLL like this:
ConfigurationManager.OpenExeConfiguration("Plugin.dll");
var name = AppSettings.Settings["Name"].Value;
Or you can load the app.config for your main application DLL and put all the appsettings in that file. In this case you should do:
var config = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location);
var name = config.AppSettings.Settings["Name"].Value;
Both solutions should be called from within of you Plugin implementation for example in constructor. Or by first call to some lazy loaded configuration property.
we are trying to hot swap (update) assemblies, the normal workflow is that we make some changes, build the assembly, do some changes and build again and in an ideal world the host app would get the new version of the assembly (with the updated types).
Here's our small plugin loader class:
public class PluginLoader<T>
{
private CompositionContainer _compositionContainer;
private RegistrationBuilder _registrationBuilder;
private DirectoryCatalog _catalog;
[ImportMany(AllowRecomposition = true)]
public IList<T> Plugins { get; set; }
public PluginLoader(string pluginsDirectory)
{
Plugins = new List<T>();
SetShadowCopy();
_registrationBuilder = new RegistrationBuilder();
_registrationBuilder
.ForTypesDerivedFrom(typeof(T))
.SetCreationPolicy(CreationPolicy.NonShared)
.Export<T>();
_catalog = new DirectoryCatalog(pluginsDirectory, _registrationBuilder);
_compositionContainer = new CompositionContainer(_catalog, CompositionOptions.DisableSilentRejection);
_compositionContainer.ComposeParts(this);
}
public void Reload()
{
_catalog.Refresh();
_compositionContainer.ComposeParts(this);
}
private static void SetShadowCopy()
{
AppDomain.CurrentDomain.SetShadowCopyFiles();
AppDomain.CurrentDomain.SetCachePath(Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "ShadowCopyCache"));
AppDomain.CurrentDomain.SetShadowCopyPath(Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "Plugins"));
}
}
We have code to recognize a new plugin dropping into the plugins folder using FileSystemWatcher, and we call Reload when that happens, but the new versions of assemblies aren't actually loaded.
Any pointers?
Notes:
No new or deleted types are recognized, it's as if it doesn't recognize the new assembly at all.
We checked and there are no composition and other errors either, so we are a bit lost :D
It is important to note that if we build the same non recognized assembly with a different compiler(Roslyn), then it is recognized (which points to nothing being badly setup, just that the assembly needs to be somehow different )
The methods called in SetShadowCopy are deprecated. You cannot enable ShadowCopy on an existing AppDomain. For an example on how to enable ShadowCopy on a new AppDomain, Have a look at this answer.
DirectoryCatalog.Refresh does update already loaded assemblies. It only checks for file deletions and additions. Have a look at this answer for a crude work-around. Note thought that I'm not sure if such an approach is thread-safe or production-ready since I have only tested simple scenarios. Another approach would be to create your own DirectoryCatalog that can handle updates as well. The MEF source code is available (as it is for the rest of the framework). The tricky part is thread-safety since the DirectoryCatalog implementation is using Microsoft's internal classes for locking.
trying to put up a nontrivial MEF application.
I wonder - is there a way for an object being composed to get a pointer to the ObjectComposer used to compose it?
The scenario is that the obejct may have to make inner compositions. I can push all links so far wia IMPORT attrobites on properties or the constructor, but I miss a way for the composed object to get the object composer (so it could create a sub-composer for it's inner items).
Any standard / best practice way?
I assume that by "ObjectComposer", you mean the MEF container. I am not aware of a class or concept named ObjectComposer .
(edit: the CompositionContainer documentation now explicitly mentions that you shouldn't put a container in itself. Hence the strike-through below.)
In your application start-up code, the container could register itself in itself:
CompositionContainer container = new CompositionContainer(...);
container.ComposeExportedValue<ExportProvider>(container);
Then you could import it anywhere like this:
[Import]
public ExportProvider Composer { get; set; }
However, what you are asking for here is a Service Locator, which has certain disadvantages. It is better to import more specific services, like an abstract factory.
ExportFactory could also be helpful here, but that didn't make it into the .NET 4 release. You'll have to use the latest codeplex release of MEF if you want to try that.
This is how I do it:
Lots of my Exporters Import IMain.
[Import(typeof(IMain))]
public IMain Main { get; set; }
IMain is used as a container for an event service and the original composition service.
public interface IMain
{
ICompositionService CompositionService { get; set; }
EventPublisher Events { get; set; }
// more...
}
The initial composition is like this:
AggregateCatalog Catalog = new AggregateCatalog();
Catalog.Catalogs.Add(new DirectoryCatalog(Application.StartupPath));
CompositionContainer Container = new CompositionContainer(Catalog);
this.CompositionService = Container; // <- here I'm setting the IMain composition service
Container.ComposeParts(this);
Then when I want to compose a new object, I just pass it to the composition service:
MainPanel discoverPanel = new MainPanel();
Main.CompositionService.SatisfyImportsOnce(discoverPanel);
discoverPanel.Show();
What are the best practices to create a site, with ability to develop plugins for it?
Like you want to create a blog module, and you want users or co-developers to add plugins to extend this module functionality.
Update:
Thanks for the ultra speed answers, but I think this is over kill for me. Isn't there a simpler solution, like I have seen blogengine plugin creation system is you just have to decorate the class plugin with [Extension].
I am kind of mid core developer, so I was thinking of base class, inheritance, interfaces, what do you think ?
Edit
I completely rewrote my answer based on your question edit.
Let me show you just how easy it is to implement a plugin architecture with just the minimal steps.
Step 1: Define an interface that your plugins will implement.
namespace PluginInterface
{
public interface IPlugin
{
string Name { get; }
string Run(string input);
}
}
Step 2: Create a plugin that implements IPlugin.
namespace PluginX
{
using PluginInterface;
public class Plugin : IPlugin
{
public string Name
{
get { return "Plugin X"; }
}
public string Run(string input)
{
return input;
}
}
}
Step 3: Run the plugin.
namespace PluginTest
{
using System;
using System.IO;
using System.Runtime.Remoting;
using PluginInterface;
class Program
{
static void Main( string[] args )
{
string pluginFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PluginX.dll");
ObjectHandle handle = Activator.CreateInstanceFrom(pluginFile, "PluginX.Plugin");
IPlugin plugin = handle.Unwrap() as IPlugin;
string pluginName = plugin.Name;
string pluginResult = plugin.Run("test string");
}
}
}
Keep in mind, this is just the basic, most straightforward example of a plugin architechure. You can also do things such as
create a plugin host to run your plugin inside of it's own AppDomain
choose either interfaces, abstract classes, or attributes to decorate your plugins with
use reflection, interfaces, IL-emitted thunks or delegates to get the late binding job done
if your design so dictates.
It's valuable to separate technical and architecturas perspectives:
In code level MEF (Managed Extensibility Framework) is a good start. Here is a simple example.
Any other DI (Dependency Injection framework) can work well to (ie. Unity)
And it's good to see this problem in architectural level:
Web Client Software Factory from p&p. Here are not only technical but arcihtectural informations about "How to create composite web applications?". See examples.. There is Modularity Boundle package.
Spring Framework.
I think it's a fast and efficient if you read&try some of those frameworks. And ofcoz read the source if you find something interessing.
Edit
if you are searching for an extensible blog engine then try Blog Engine first. It's from ASP.NET community.
This sounds like a job for the Managed Extensibility Framework from Microsoft. It's in a preview release at the moment but it would seem to be a better bet than rolling your own framework for this. There are links to guides about how to use this on the site there.
If you would like to see a real, open source application that impliments this archecture take a look at DotNetNuke.