Accessing the configuration file across application domain - c#

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.

Related

MvcSiteMapProvider : How to load extra web.sitemap files from MEF-enabled module in MVC 4

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.

WPF Prism access to defined info like some class objects inside Module from main project

I have a WPF Prism solution in VS and want keep some settings of my prism module inside an object in the module project (i load this settings from a file), then i created my prism module class (AModule.cs) like this:
Inside my module project (AModul project):
[Export]
[Module(ModuleName = "AModule")]
public class AModule : IModule
{
ModuleSettingsModel ModuleSettings { get ; set; }
public AModule()
{
// throw new NotImplementedException();
}
public AModule(IUnityContainer container)
{
// Some codes ....
}
~AModule()
{
// some codes
}
public void Initialize()
{
var regionManager = _container.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("WorkspaceRegion", typeof(ModuleAView));
ModuleSettings = DataFileManager.LoadModuleData();
}
It works well and i can use my settings inside AModule
Inside Main WPF Project:
But i need access this settings (ModuleSettings property) in my Main WPF project too. For example i need access to ModuleA»ModuleSettings in my Bootstrapper class of my WPF application. I need do some workd base on each module settings in my main project...
My question is what solutions are there to do? Should i register any type? Where? How?
Note1: ModuleSettings* is inherited from IModuleSettings and IModuleSettings is inside Infrastructure project.
Note2: I load my modules dynamically into prism (my main WPf project has not any reference to AModule);
You should put the ModuleSettings into a class library project that is referenced by the various modules/projects. You could use the singleton pattern to only load and maintain one instance of the settings.
After read GlenThomas answer & #BenediktSchroeder's first comment in question and reading some additional references like this & this, i could found & implement a good way for my question:
I solved it by adding a new custom Prism Service
Create an Interface for our service class (eq. ISettingManager) in Infrastructure Project. Modules and Main App need reference to Infrastructure Project
Create an Class for our service (eq. SettingManager)
Define one property in ISettingManager to keep ModuleSetting like:
Dictionary<string,ModuleSettingsModel> ModuleSettingsPairs{get;set;}
Add some methods in ISettingManager to Get, Add and Remove setting to/from the ModuleSettingsPairs dictionary.
Register the new service type in ConfigureContainer inside the bootstraper as Singlton like:
RegisterTypeIfMissing(typeof(IAddonSettingsManager), typeof(AddonSettingsManager), true);
Obtain reference to the IAddonSettingsManager service instance in the module project or main app project, it can do by one of following ways:
Declaratively obtain references to services using constructor injection
Programmatically obtain references to services (by use the Resolve method of the Unity container)
Finally, play with custom Get, Add and Remove methods of our service instance (SettingManager)

C# Plugin Implementation Query

I am trying to write a plug-in for an application. The only thing I am provided with is a dll resource file which defines an interface; here's the code:
using System;
using System.Drawing;
namespace App.Plugin.Resources.Interface
{
public interface IAppPlugin
{
string Name { get; set; }
string Description { get; set; }
string Author { get; set; }
string Version { get; set; }
Icon MenuIcon { get; set; }
EventHandler PluginEventHandler { get; set; }
}
}
I then created a class that implemented this interface, made it display a message box, compiled the dll, placed it in the Plugins folder of the application and when the application executed and launched the plugin, it did display the message.
It seems to me that the software offers the means to execute external code (through the plugin system), but doesn't actually give access to any of the application's properties or methods.
Considering the above, my question is: Am I able to interact with the host process in any other way (e.g. get informed when a menu item is selected or even add a menu item myself to the main GUI) with the given resources or does this plugin system just act as an application launcher (by executing the code in the dll I'm providing)?
This seems just an application launcher, not a real plugin, unless there is some strategy implemented by convention: maybe the app looks at the plugin constructor with reflection, and pass some interfaces to the host system, or it looks for some properties marked with some custom attributes to pass some entry points.Another possible vehichle to pass the main application entry points is the PluginEventHandler, try to see in debug what you receive when the plugin is invoked. In addition, try to look with some tools as ILspy to see if there is something more in the plugin instantiation.

App.config multi-project access strategies

My current solution has 3 project with 2 app.config (one for common settings and another for service settings). As of now I'm simply creating static classes to act as a mediator to access values. I do this so I don't have to write ConfigurationManager.AppSettings["SomeKey"] everywhere. This works fine until you want to access an app.config file from a different project.
Here is what I'm currently doing (all properties omitted for brevity).
public class ServiceConfiguration
{
public static readonly string SyncEvery = ConfigurationManager.AppSettings["SyncEveryMinutes"];
}
How can I access an app.config file located in another project? I thought perhaps setting VS to copy the file to the output directory would do the trick however my configuration object is still null.
I can't imaging many good reasons to read another app's configuration in the first place, it just opens a can of worms that isn't worth dealing with.
Expose a class that exposes the project's configured values as properties, and access them from a consuming class.
public class FirstProjectClass
{
public static int SyncEveryMinutes
{
get { return (int)ConfigurationManager.AppSetting["SyncEveryMinutes"] };
}
}
public class SecondProjectClass
{
public void ShowConfigedValue()
{
Console.Writeline("Syncing every {0} minutes", FirstProjectClass.SyncEveryMinutes);
}
}
if you've got complex configuration requirements you can also look into custom configuration sections
ConfigurationManager.OpenExeConfiguration can be helpfull:
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.openexeconfiguration.aspx
Also: what Jason said - it is usually a bad idea.

Sharing settings between applications

I have multiple .NET assemblies that all need to share common user settings, such as preferences, user names, etc. One is a WPF application, another is a console application, and the third is an Office Add-in. All of these settings are user-scope.
Only the WPF application needs to be able to change settings. The rest just read them.
Ideally, I'd like to use the .NET configuration framework. I'm not sure how to do this though. If I add Settings to the WPF application, how can the other applications find the user.config file?
Is it just easier to create a class library and use IsolatedFileStorage and serialize my settings?
Any advice would be greatly appreciated.
You can implement your custom settings class, inheriting ApplicationSettingsBase. As a good start, you can add the default User Settings file to a sample project (Right click on the project -> Properties -> Settings -> This project does not contain a default settings file. Click here to create one.). Add a user-scoped setting and investigate the structure of the designer-generated Settings.Designer.cs file:
namespace ConsoleApplication1.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("John Doe")]
public string Name {
get {
return ((string)(this["Name"]));
}
set {
this["Name"] = value;
}
}
}
}
In your custom implementation, you will not be limited to the designer-generated access modifiers, so you can implement the Settings class as internal with internal setters, visible only to the needed assemblies, or whatever fits your needs.
Of course, you can always implement your custom serialize/deserialize mechanism, but you will lose the funcionality provided by ApplicationSettingsBase's Updgrade, Reload, and Reset methods. If you don't need any of these, this could be the cleaner approach.
I would recommend you to create service to provide and update user info and or preferences. It will be better architecture, cleaner solution and it will be easier to maintain and extend.

Categories

Resources