Alternatives to reading config files from class libraries at design time? - c#

I have a WinForm project that contains several UserControls. This WinForm project has a reference to an assembly (lets call it lib.dll) that is created from another project (Class Library) that exists in a different solution.
Now, several of the UserControls make calls into lib.dll that return values from the app.config file. At runtime lib.dll works fine and returns the necessary data but at design time, I am getting an exception from lib.dll because the app.config sections are NULL (the exceptions are by design).
Now I could go through each control and wrap any code that calls into lib with
if(!DesignMode) { //code }
But that is a lot of controls to go and apply that to. Is there something I can do globally that would be more elegant then testing the DesignMode property?
Edit
In response to the two comments left below: the solutions provided don't appear to work. The assembly that is causing me a problem lives in the same directory as the app.config. The general directory structure looks like this
References Folder
Configurations (Folder)
appsettings.config
app.config
lib.dll
app.config pulls in several other config files (appsettings, cnx strings, etc) which reside in the Configurations directory. In the case of my exception the value I am trying to get resides in one of these ancillary config files that is referenced by app.config.

This is an interesting question. A solution could be to create in lib.dll a static class like this one :
public static class Config
{
private static readonly _param1;
static Config()
{
_param1 = ConfigurationManager.AppSettings["Param1"] ?? "Your default value";
}
public static string Param1
{
get { return _param1; }
}
}
Then, in your code, insted of writing ConfigurationManager.AppSettings["Param1"], you will use Config.Param1. So you won't need to test the property DesignMode.

There are so many ways to do this, IMHO.
One thought that immedidately comes to mind would be to use an inheritance-based approach for the user controls in question? That way, in the base class, you can put that if (DesignMode) check in, and do the correct branching from there.
// if i were to visualizeyour lib.dll data initializer call like this:
class BaseUserControl
{
// i'm guessing that you initialize the data somehow...
void InitializeData()
{
if (!DesignMode)
{
InitializeDataLocal();
}
}
protected virtual InitializeDataLocal()
{
// whatever base behavior you want should go here.
}
}
// in the derived classes, just put the code you currently have for
// fetching the data from lib.dll here...
class UserControl : BaseUserControl
{
protected override InitializeDataLocal()
{
// fetch from lib.dll...
// optionally invoke some base behavior as well,
// if you need to...
base.InitializeDataLocal();
}
}

Related

SimpleInjector - Registering plugins dynamically with constructor values from config

According to the documentation of SimpleInjector one can use the following code to load assemblies dynamically
string pluginDirectory =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
var pluginAssemblies =
from file in new DirectoryInfo(pluginDirectory).GetFiles()
where file.Extension.ToLower() == ".dll"
select Assembly.Load(AssemblyName.GetAssemblyName(file.FullName));
container.Collection.Register<IPlugin>(pluginAssemblies);
Let's say each plugin has its properties (name, language, ...) which are specifed in App.Config in the following manner
<Plugins>
<Element name="SamplePlugin" filename="SamplePlugin.dll" language="pl-PL" modeId="1" />
</Plugins>
These values are necessary as they are used in constructor of every plugin.
Currently there is a PluginSettings class which gets ConfigurationElements looping through a ConfigurationElementCollection and later on in the main ViewModel there is a loop going through the collection contained in the mentioned settings.
public class PluginSettings
{
readonly Configuration _config = ConfigurationManager
.OpenExeConfiguration(ConfigurationUserLevel.None);
public LoaderSection PluginAppearanceConfiguration
{
get
{
return (LoaderSection)_config.GetSection("pluginSection");
}
}
public PluginsCollection PluginsCollection
{
get
{
return PluginAppearanceConfiguration.PluginElement;
}
}
public IEnumerable<PluginElement> PluginElements
{
get
{
foreach (PluginElement selement in PluginsCollection)
{
if (selement != null)
yield return selement;
}
}
}
}
Current loading plugins in ViewModel
try
{
var factory = ModuleLoader
.LoadPluginFactory<PluginFactoryBase>(Path.Combine(pluginsPath, plug.Filename));
var configAppSettings = new ConfigAppSettings(plug.FactoryId, plug.ModeId, plug.Language);
Application.Current.Dispatcher.BeginInvoke((Action)delegate
{
plugins.Add(new Plugin(plug.Name, factory,
configSettings, configAppSettings));
});
}
catch (ReflectionTypeLoadException ex)
{
foreach (var item in ex.LoaderExceptions)
{
MessageBox.Show(item.Message, "Loader exception",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
Each plugin has its own PluginFactory defined. Code is a few years old and wasn't written by anyone still present. Further refactoring would include making common Factory in the main application as I don't see reasons for the current state. Plugins are listed in the main app and are initialized (by plugins PluginFactory) only when the specific one is selected (clicked) and then displayed.
How to adapt the code to follow good etiquette and process plugins in composition root and inject them?
This question is hard to answer, because it depends on what the ViewModel needs to do with this information.
When it comes to Simple Injector, however, all plugins that you might use during the execution of your application need to be registered up front. So, that means that if the ViewModel decides to only use 2 out of 5 defined plugins, you still need to register all 5. (* see note below)
But it likely means you need to do a few things:
Load the plugin information from the config
Use this plugin information to register the plugins in a collection in the order they are listed
Supply the plugin information and an IEnumerable<IPlugin> to the ViewModel.
Let the ViewModel pick the proper plugin from the collection based on the plugin information and the runtime data given at that time
For instance, if the ViewModel requires the plugin with language pl-PL, it needs to find out what the index is of the pl-PL plugin from the plugin information and use that index to get that plugin from the IEnumerable<IPlugin>, e.g.:
var info = pluginInformation.First(p => p.Language == "pl-PL");
int index = pluginInformation.IndexOf(info);
IPlugin plugin = plugins.ElementAt(index);
plugin.DoAwesomeStuff();
Note that with Simple Injector, a call to ElementAt on an injected IEnumerable<T> will be an optimized O(1) operation. So even if the collection contains hundreds of plugins, only one plugin will be created at that time.
*This isn't completely true, because you could load and register plugins just-in-time, but this is an advanced topic which you likely don't need.

Read Settings from Dependent Project in C#

I just want to start by saying that I've done a lot of research but couldn't find an answer, hence the post.
I'm adding user settings functionality to my app which works as a plugin inside a common off the shelf program for architecture (called Autodesk Revit). The main project (let's call it MainProj) has several dependencies including a project that handles logging and usage (let's call it Loggers). I created a Settings file inside the Loggers project with the goal to have users change the logging level from Error to Debug when there are issues so I can ask them to make the change and send me the log.
The issue I'm facing is that when I change the log level directly inside the config file and re-run the command from within Revit, the change doesn't get translated into the log, as if the log level is somehow compiled during design and is never changed.
I actually tried to reproduce the problem in a simpler way and created a little console program and I'm facing the same issue. Below is the code from the Loggers project.
namespace Loggers
{
public static class Logger
{
public static string RunMe()
{
if (Properties.Settings.Default.LogMode == "Debug") { return "DEBUG"; }
else return "NOTHING";
}
}
}
I then changed the LogMode property from Debug to anything else in the config file but the console kept on returning DEBUG.
static void Main(string[] args)
{
Console.WriteLine(Logger.RunMe());
Console.Read();
}
I also tried changing the setting from user to application and editing its value in the config file and re-running the command but the outcome was the same.
Any help would be very much appreciated. I've been stuck on this for a while. Thank you.
Thanks to #BurnsBA, the link you shared had comments saying that the user.config lives in a different folder and it's not created until the user changes a setting. This made me understand that there wasn't a point in manually editing the app.config and expect the settings to work.
I then did some testing by creating a simple form with a checkbox linked to the Property I wanted to change and the user.config file gets created straight after I call the Save() method on the Properties.
private void btnOK_Click(object sender, EventArgs e)
{
if (chkDebugMode.Checked == true)
Loggers.Properties.Settings.Default.LogMode = "Debug";
else Loggers.Properties.Settings.Default.LogMode = "Error";
Loggers.Properties.Settings.Default.Save();
Close();
}

C#, Context Object with Method that takes Interface to External DLL, Throws MissingMethodException

I have a service which acts as a scripting engine for normalization of content. The service extends its scripting language by dynamically loading DLLs at startup, passing a context object interface to a Load() method in each DLL. The context object provides the DLL with methods for Binding string names to factory objects, commands/functors, script object wrappers, etc.
I am having a problem where when the Load() method of one of the DLLs is called it throws a MissingMethodException if it attempts to call one of the binding methods. The method is one which takes an interface which is declared in another DLL, and I suspect something is going on with the method signature when passing the context object through Type.GetMethod.Invoke().
It should be noted that the exception is only thrown when dynamically loading the DLL via Assembly.LoadFile(). Referencing the DLL in the main process and using reflection to get the class/method, then dynamically invoking it from there causes no exception to be thrown.
My code/project reference setup ...
Content.dll:
References nothing
public class ContentPackage { ... }
public interface IContentDataSource
{
ContentPackage Receive();
void Send(ContentPackage content);
}
public interface IContentDataSourceFactory
{
IContentDataSource CreateInstance(string param);
IEnumerable<IContentDataSource> CreateInstances(string param);
}
public class ContentXmlDataSource : IContentDataSource
{
ContentPackage IContentDataSource.Receive() { ... }
void IContentDataSource.Send(ContentPackage content) { ... }
public class Factory : IContentDataSourceFactory
{
IContentDataSource IContentDataSourceFactory.CreateInstance(string param) { return new ContentXmlDataSource(param); }
IEnumerable<IContentDataSource> IContentDataSourceFactory.CreateInstances(string param) { ... }
IContentDataSourceFactory.CreateInstance(
public static Factory Singleton { get { return s_Singleton; } }
private static Factory m_Singleton = new Factory();
}
}
ExtensionInterface.dll:
References Content.dll
public interface IXmlTagCommand() { ... }
public interface IExtensionBindingContext
{
void BindCommand(string name, IXmlTagCommand cmd);
void BindDataSourceFactory(string name, IContentDataSourceFactory factory);
}
Service.dll:
References ExtensionInterface.dll, Content.dll
public partial class TheService
{
public class ExtensionBindingContext : IExtensionBindingContext
{
void IExtensionBindingContext.BindCommand(string name, IXmlTagCommand cmd)
{ ... }
void BindDataSourceFactory(string name, IContentDataSourceFactory factory)
{ ... }
ExtensionBindingContext(string basePath)
{
var paths = Directory.GetFiles(basePath, "*.dll");
foreach (var path in paths)
{
Assembly assembly = Assembly.LoadFile(path);
Type t = assembly.GetType("ExtensionLibrary");
MethodInfo method = t.GetMethod("Load",
BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { this }); // Throws exception!
}
}
}
TheService()
{
(new ExtensionBindingContext()).LoadExtensions(".\DLLFolder\");
}
}
Extension.StandardContentFactories.dll: (Loaded dynamically)
References ExtensionInterface.dll, Content.dll
public class ExtensionLibrary
{
public void Load(IExtensionBindingContext context)
{
context.BindCommand("SomeCommand", XTagCommands.SomeCommand); // Fine: No exception.
context.BindDataSourceFactory("ContentXml", ContentXmlFactory.Singleton); // Causes whole function to throw exception if not commented out, preventing even the "BindCommand" call to not be reached.
}
}
The exception specifics:
Exception:
System.Reflection.TargetInvocationException, {"Exception has been thrown by the target of an invocation."}
Inner Exception:
{"Method not found: 'Void SomeNamespace.ContentService.IExtensionBindingContext.BindDataSourceFactory(System.String, SomeNamespace.Content.IContentDataSourceFactory)'."}
If I comment out the BindDataSourceFactory() line in StandardContentFactories.dll, the problem goes away. I am also successfully loading multiple other DLLs in this manner.
I've tried the following:
- Checking GAC folders for same named assemblies from project, none found
- Cleaning solution and rebuilding
- Verifying that all assemblies were using same .NET (4.0, not client profile)
- Passing the interface in a wrapper object instead, changing the method to take the wrapper: Field not found exception instead
- Passing the factory object as an "Object" (updating method parameters as well), then casting that object back into the interface that the factory object inherits: Invalid cast
Is there some kind of issue with passing objects behind interfaces to DLLs via dynamic assembly loading and invoking if that interface has a method that takes an interface which is defined in another referenced assembly? Are there possible work arounds to allow for this dynamic binding of factories from unknown DLLs if the interface issue is non-solvable?
EDIT:
After checking the modules as per mike z's suggestion, I see that their are two copies of each shared interface DLL being loaded (Content.dll and ExtensionInterface.dll); the first set of DLLs is being loaded from the service's host process bin\Debug directory; the second set of DLLs is being loaded from the bin\Debug directory of the first addon DLL that is loaded by Assembly.LoadFile().
The process uses an array of directory paths to search for addon DLLs in, and I was able to force the process to find/load the DLL that has the problem first, which caused the problem to go away. I'm guessing that all of the Assembly.LoadFile() loaded DLLs are using the set of shared interface DLLs that are loaded as a side effect of the first call to Assembly.LoadFile() (i.e. for the first addon DLL). When these DLLs load, maybe they are only including interface information for the IExtensionBindingContext.BindCommand() but not the IExtensionBindingContext.BindDataSourceFactory() and/or IContentDataSourceFactory since they are not used by that DLL.
Does anybody know if this assessment is correct? If so, is there a (standard or correct) way to force my Assembly.LoadFile() DLLs to correctly load ALL of the interface/method information from a shared library? (I'd rather not use contrived solutions like forcing the load order from a config file or creating a fake library that used all the features at the start.)

Is it possible to initialize a ConfigurationSection from an external file?

I have a custom configuration property in my app. It looks something like this:
public class OverrideConfiguration : ConfigurationSection
{
[ConfigurationProperty(PROP_ADMIN_CONNSTR)]
public StringConfigurationElement AdminConnectionString
{
get { return base[PROP_ADMIN_CONNSTR] as StringConfigurationElement; }
set { base[PROP_ADMIN_CONNSTR] = value; }
}
// .. Various other properties, but you get the idea
}
However, what I'd like is to allow the .config file to be pointed to an external file source. Something like this:
<ServiceOverrides file="Overrides.local.config" />
Now, the built-in configSource attribute is close to what I need, but it has two major issues.
Files must exist. If the file doesn't exist, it errors out.
Files must be in the current directory or in a deeper directory. In other words, I can't point to ..\Overrides.local.config
What I want is pretty much identical to the <appSettings file="..." /> configuration element. However, that attribute seems to be something appSettings implemented, and is not part of the base ConfigurationSection class.
My Question:
Is it possible to override something in ConfigurationSection that will basically read XML data from a different location? I don't want to change any other aspect of my class or do my own XML deserialization or anything. I simply want to check if a file exists, if so, load in the XML contents from that file, otherwise load in the default XML contents.
Ok, I have a working solution. I'm not sure if it's the best approach, but it does appear to work exactly how I want.
private readonly Queue<String> externalSources = new Queue<String>();
protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
{
var externalFile = reader.GetAttribute("File");
if(!String.IsNullOrWhiteSpace(externalFile))
{
externalSources.Enqueue(externalFile);
}
base.DeserializeElement(reader, serializeCollectionKey);
}
protected override void PostDeserialize()
{
base.PostDeserialize();
// Override data with local stuff
if (externalSources.Count == 0) return;
string file = externalSources.Dequeue();
if (System.IO.File.Exists(file))
{
var reader = XmlReader.Create(file);
base.DeserializeSection(reader);
}
}
First, I trap the DeserializeElement event, which happens when we read the <ServiceOverrides> element. We check if it has a File attribute, and if so we add it to a queue of external sources to load.
Next, we trap the PostDeserialize event, which gets called after all the local XML is parsed. If there's an external source in the queue, we dequeue it, check if it actually exists, then create an XmlReader with the contents of that file. Now we can simply call DeserializeSection again and pass in our new reader. The ConfigurationSection class is smart enough to just overwrite or append any new data to the existing configuration. What I get at the end is an aggregation of both configuration files, where the include file wins in the event of a duplicate.
Now, what's this nonsense with the queue? Well, it seems every time you call DeserializeSection, it'll call PostDeserialize again. So, if we simply trapped PostDeserialize, check the File attribute, and call DeserializeSection again, we'd get in an infinite loop. We could just use a flag to remember if we already loaded the external file, but a queue has the added benefit of allowing the include file to load more include files (not that I'd ever want to do that, but you might).
Tips: This will probably work fairly well, and is simple to understand, but if you're using it in production code, there's a few things you could improve on. First, externalSources doesn't really need to be a queue, since these calls aren't actually recursive. You can probably just use a string, and set it to null after you're done processing that file. Second, this could cause an infinite loop in the event of a circular include chain. You could create a List<T> of previously included files, then check if the include already exists in that list before adding it to the queue.
Hope this helps someone!

Ninject + Auto Discovery

Here's the deal: I want to create a C# console app. When run, this app will look in a particular folder for dll's with classes that implement a particular interface, and then run a method on those dll's.
I haven't done this before but from my reading that should be "easy enough" from an IoC/Ninject perspective. I think you can do something with kernel.Bind() to load assemblies of a certain interface in a certain directory. I think/hope I can figure that part out (if you know otherwise, please do tell!).
But here is my quandary.
Here is a visual to help first:
MainProgramFolder
-- MainProgram.exe
-- MainProgram.exe.config
-- LibraryFolder
----- Library1Folder
--------Library1.dll
--------Library1.dll.config
----- Library2Folder
--------Library2.dll
--------Library2.dll.config
The dll's that implement this interface are technically stand alone apps -- they are just libraries instead of exe's (or, rather, I'd like them to be for IoC purposes). I'd like for them to be run in their own context, with their own app.configs. So for example, MainProgram.exe would bind the ILibrary interface to classes inside Library1.dll and Library2.dll because they implement ILibrary. But inside Library1, it calls ConfigurationManager to get its settings. When I call Class.Method() for each of the bindings from MainProgram, how can I ensure they are referencing their own .config's and not MainProgram.exe.config? (Also, fwiw, these additional libraries will likely not be a part of the assembly or even namespace of the main programs -- we're basically providing a drop folder for an application to kind of "subscribe" to the main program's execution.)
IOW, I know you can attach an app.config to a class library but I wouldn't know how, after the bindings have been resolved from the IOC, to make those dll's "see" its own config rather than the main program's config.
All thoughts appreciated!
Thanks
Tom
First, to load and bind all of your classes you'll need ninject.extensions.conventions, and something like this:
var kernel = new StandardKernel();
/*add relevant loop/function here to make it recurse folders if need be*/
kernel.Bind(s => s.FromAssembliesMatching("Library*.dll")
.Select(type => type.IsClass && type.GetInterfaces().Contains(typeof(ILibrary)))
.BindSingleInterface()
.Configure(x=>x.InSingletonScope()));
To make each instance load its configuration as if it was the entry point you will need to run it in a new app domain. Your ILibrary implementation needs to inherit MarshalByRefObject and be Serializable so that it will run correctly in the alternate appdomain
[Serializable]
public class LibraryA :MarshalByRefObject, ILibrary
You can then add this activation strategy to your kernel that will cause it to swap out instances of ILibrary with an instance loaded in an alternate appdomain with your config file convention before they are returned.
public class AlternateAppDomainStrategy<T> : ActivationStrategy
{
public override void Activate(IContext context, InstanceReference reference)
{
if (reference.Instance.GetType().GetInterfaces().Contains(typeof(T)))
{
var type = reference.Instance.GetType();
var configFilePath = type.Assembly.GetName().Name + ".dll.config";
var file = new FileInfo(configFilePath);
if (file.Exists)
{
var setup = new AppDomainSetup() { ConfigurationFile = file.FullName, ApplicationBase = AppDomain.CurrentDomain.BaseDirectory };
var domain = AppDomain.CreateDomain(type.FullName, null, setup);
var instance = domain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
reference.Instance = instance;
}
else
{
throw new FileNotFoundException("Missing config file", file.FullName);
}
}
}
}
And Add it to your kernel
kernel.Components.Add<IActivationStrategy, AlternateAppDomainStrategy<ILibrary>>();
From there you can simply instantiate your ILibrary instances and call methods on them. They will load in their own app domains with their own configs. It gets a lot more complicated if you need to pass things in/out of the instance either via methods or constructor, but from the sound if it you don't so this should be OK.
var libs = kernel.GetAll<ILibrary>();
foreach (var lib in libs)
{
lib.Method();
}

Categories