Iterate through PRISM modules and execute respective "finalize" methods - c#

My app consists of a Shell and many modules which are loaded from a directory. I'd like to add a Finalize() method to each of these modules so they can perform their own required cleanup/shutdown code such as closing files, freeing sockets, perform saves/backups, etc.
I know this concept can be easily achieved with event aggregation and publishing an event on shutdown, but I'd much rather iterate through each module and call this Finalize() method which returns a status int showing whether or not the module encountered any problems during finalization.
So far I've created the following simple interface, and had the appropriate modules implement it.
public interface IFinalizeModule
{
int Finalize();
}
Is it possible to iterate through the modules in the module catalog and do something like this without creating additional instances of them? The closest thing I've found is in this post but I can't wrap my head around his explanation
I would place each module's Start method in a separate component (IStartable), register each component in the container with a different Id and resolve/import an IEnumerable to get all instances that have the start method.
This code doesn't work, but it's what I'm trying to achieve
foreach (IModule module in ModuleCatalog)
{
int exitcode = module.Finalize();
if (exitcode != 0)
{
// do something
}
}

Modules are short lived. They are created, initialized and garbage collected. The only job of a module is to perform registrations.
If you have services that need to be shut down when the application is shut down, you have to notify those services if you're about to shut down. Either through an event they subscribe to or by creating a RegistryOfServicesThatNeedToBeShutDown.
Example expanding on my comment (if you insist on cleaning up on module-level):
class MyModule : IModule
{
public void Initialize()
{
_container.RegisterType<IMyService, DisposeMe>( new ContainerControlledLifetimeManager() );
_container.RegisterType<IMyOtherService, DisposeMeToo>( new ContainerControlledLifetimeManager() );
_container.RegisterType<IModuleCleanUp, CleanUpMyModule>( nameof( MyModule ) );
}
private class CleanUpMyModule : IModuleCleanUp
{
void Finalize()
{
_container.Resolve<IMyService>().Dispose();
_container.Resolve<IMyOtherService>().Dispose();
}
}
}
Now you have a regular module and its clean-up-code next to each other. But the code looks ugly a bit, and there are things to be aware of:
if someone resolves IMyService after the clean-up, you're done for
you don't even know whether the type registered as IMyService requires clean-up: another module that's initialized after MyModule might register a different type for IMyService

Related

How to clean up instances created by SimpleInjector during verification?

As a part of creating my SimpleInjector container, I've followed recommended practices and called container.Verify() to check that my type registrations make sense.
This works well and has caught a number of errors that I've made - but it also creates debris that lingers around afterwards that I'd like to clean up.
One of my classes is a singleton event hub that's used to route messages between other transient components; these other components accept the event hub in their constructor, create a subscription to receive the messages they're interested in receiving, then Dispose() the subscription when they're finished.
The call to container.Verify() creates one of each kind of object, resulting in a number of these otherwise transient instances lingering around because the event hub still knows about their subscriptions.
At the moment I've worked around the problem by manually terminating all subscriptions immediately after the Verify() call, before the application starts up. However, this feels like a problem that must be already solved, though I haven't been able to find an answer in the docs, here on Stack Overflow, or by searching.
Perhaps using a scoped lifestyle is the solution? They didn't seem relevant because I'm building a WPF application, but if I knew the answer I wouldn't be asking here!
Update, 12 Jan - As requested by #steven, here's some code to demonstrate my issue.
I tried (and failed) to demonstrate the issue with something that was both compilable and short enough to share inline; instead I'm showing some code excerpts from the actual project. If you want to see the whole thing, the WordTutor project is on GitHub.
At the core of my application I have a singleton IReduxStore<T> which both encapsulates application state and acts as a kind of event hub. Other classes subscribe to the store in order to be proactively notified when the application state changes.
Here is IReduxStore<T>, pared down to the essentials:
// IReduxStore.cs
public interface IReduxStore<T>
{
T State { get; }
void Dispatch(IReduxMessage message);
IDisposable SubscribeToReference<V>(
Func<T, V?> referenceReader,
Action<V?> whenChanged)
where V : class, IEquatable<V>?;
}
Subscriptions implement IDisposable as a convenient and idiomatic way for deterministic cleanup when the subscription is no longer required.
The store is registered as a singleton, bound to a specific type of state:
// Program.cs
container.RegisterSingleton<
IReduxStore<WordTutorApplication>,
ReduxStore<WordTutorApplication>>();
The implementation of IReduxStore<T> stores all the subscriptions:
private readonly HashSet<ReduxSubscription<T>> _subscriptions
= new HashSet<ReduxSubscription<T>>();
They're removed from the HashSet when disposed.
Many of my ViewModels accept IReduxStore<WordTutorApplication> to their constructors so they can subscribe to updates:
// VocabularyBrowserViewModel.cs
public sealed class VocabularyBrowserViewModel : ViewModelBase
{
private readonly IReduxStore<WordTutorApplication> _store;
private readonly IDisposable _screenSubscription;
private readonly IDisposable _vocabularySubscription;
public VocabularyBrowserViewModel(IReduxStore<WordTutorApplication> store)
{
_store = store ?? throw new ArgumentNullException(nameof(store));
// ... elided ...
_screenSubscription = _store.SubscribeToReference(
app => app.CurrentScreen as VocabularyBrowserScreen,
RefreshFromScreen);
_vocabularySubscription = _store.SubscribeToReference(
app => app.VocabularySet,
RefreshFromVocabularySet);
// ... elided ...
}
// ... elided ...
}
ViewModels are registered as transient because each window needs a unique instance:
// Program.cs
var desktopAssembly = typeof(WordTutorWindow).Assembly;
container.Collection.Register<ViewModelBase>(desktopAssembly);
The ViewModels release their subscriptions proactively when they are no longer needed:
// VocabularyBrowserViewModel.cs
private void RefreshFromScreen(VocabularyBrowserScreen? screen)
{
if (screen == null)
{
_screenSubscription.Dispose();
_vocabularySubscription.Dispose();
return;
}
Selection = screen.Selection;
Modified = screen.Modified;
}
When Verify() is called on the SimpleInjector container, an exemplar of every object is created, including the singleton IReduxStore<T>. The transient viewmodels (such as VocabularyBrowserViewModel shown above) are also created, but those instances remain live because their subscriptions are still held by the store.
I tried implementing IDisposable on the ViewModels, but because their lifestyle is transient, the only effect was to generate an additional warning when Verify() was called.
Update II, 12 Jan:
The workaround I have at the moment is to manually clear all the subscriptions as a part of application startup, after the container has been successfully initialized:
var store = (ReduxStore<WordTutorApplication>)
container.GetInstance<IReduxStore<WordTutorApplication>>();
store.ClearSubscriptions();
This feels like a nasty hack. First it needs to explicitly cast to the implementation type, then it calls a method that otherwise wouldn't need to exist at all.
Try setting EnableAutoVerification to false in Simple Injector 5.0 (https://simpleinjector.org/ReferenceLibrary/html/P_SimpleInjector_ContainerOptions_EnableAutoVerification.htm)

Winforms IOC Container - Composition Root

I've recently been dabbling a bit with IOC Containers (LightInject in my case).
I've been reading that you should only need to use the container ONCE, on startup, and no where else. This is what I'm struggling to understand. If I can only reference the container in a bootstrap/startup method, how is it possible to resolve what I need, elswhere in the project, or at runtime if the class depends on user input.
So In my Traditional Windows Forms App, on Form Load Say, I would Bootstrap Lightinject as per the below code. It's only an arbitrary example, it's more the premise I need to get my head around.
I might be missing something here entirely, or just not getting it. But how am i supposed to resolve dependancies, If i can't use/not supposed to reference or use Container.GetInstance/Resolve/{Choose IOC Syntax Here}, and only in the composition root.
For Instance, Say I have two buttons and a TextBox on my form. The first button gets me an ILoader (below code), and the second button loads up a file viewer (ILoader, below code), whose file name is what is entered into the textbox on the winform.
Without An IOC Container I would do the following (let's just assume its put in the click event)
Button 1 Click Event :
ISplitText MyStringFunc = new WhateverImplementsIt();
Button 2 (gets the file reader based on textbox input)
ILoader MyLoader = new FileReaderImplementation(TextBox1.Text);
Using LightInject, I'm surely compelled to do the following:
Button1 Click:
ISplitText Splitter = Container.GetInstance<ISplitText>();
Button 2 Click
var LoaderFunc = Container.GetInstance<Func<string, ILoader>>();
ILoader l2 = LoaderFunc(TextBox1.Text);
Am I Incorrect? In A large project I would have Container.GetInstance, peppered all over the place, in the main form file and elsewhere surely, so how can i only reference the container in ONLY 1 spot, in the form of bootstrap, am i missing a magic piece of the puzzle?
In all the sample apps I have seen it's all done in one simple console app, in the Main function. All these apps follow the format of:
Container = new Container();
Container.Register<IFoo,Foo>();
Container.Register<IBar,Bar();
var Resolved = Container.GetInstance<IFoo>();
Well, I understand all that, and it's extremely simple. It's once you start adding a bit of complexity to the app itself, I'm lost as to how to get the instances without making the Container itself public, or static, or accessible in some way,shape or form and then calling Container.GetInstance in a million places (which apparently, is a big no no). PLEASE HELP!
Cheers,
Chud
PS - I am not concerned about "abstracting the container" itself. so would prefer to only focus on increasing my understanding of the above.
public class BootStrapIOC
{
public ServiceContainer Container;
public BootStrapIOC(ServiceContainer container)
{
Container = container;
}
public void Start()
{
Container.Register<ISplitText, StringUtil>();
Container.Register<string, ILoader>((factory, value) => new FileViewByFileName(value));
}
}
//HUH? How can i NOT use the container??, in this case in the button_click
ILoader Loader = Container.GetInstance<Func<string, ILoader>>();
ILoader l2 = Loader(TextBox1.Text);
ISplitText Splitter = Container.GetInstance<ISplitText>();
EDIT #1
OK, so, after re-reading the comments and looking at a few more examples on the interweb, I think I may finally understand it. The issue was (I think) is that i wasn't thinking "higher level" enough. I was trying to resolve my dependancies in my winforms application, AFTER the form had already been constructed,and in the form itself. When in reality, it's too late by then. I wasn't viewing the "form itself" as just another object, which needed it's dependencies injected into it.
So I bootstrap now in my Program.cs:
static class Program
{
private static ServiceContainer Container;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Container = new ServiceContainer();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
BootStrapIOC Strap = new BootStrapIOC(Container);
Strap.Start();
//This magic line resolves EVERYTHING for me required by the Form
var form = Container.GetInstance<Form1>();
Application.Run(form);
//Application.Run(new Form1());
}
}
My question now is, Is my line of thinking now correct in terms of winforms. It seems to make more sense, changing my approach to "higher up" the chain and resolving from Program.cs??
Secondly, And I'm not sure if this calls for a new question altogether, please advise as I am an SO noob.
How would I setup a factory to return the correct instance of an object? One of the original comments indicated that that would be a usage in this scenario. Let's use a contrived example.
Where I needed an object, but don't know which object until run time/user input.
My Idea:
BootStrap
Container.Register();
Factory Interface and Implementation:
Let's put some optional parameters in also, as I want to know if this is the correct/best way to do it?
public interface IFileViewerFactory
{
ILoader GetFileViewer(string FileName, string Directory = null, bool CreatingDirectory = false);
}
public class FileViewerFactory:IFileViewerFactory
{
public FileViewerFactory() { }
public ILoader GetFileViewer(string FileName, string Directory = null, bool CreatingDirectory = false)
{
if (CreatingDirectory == false)
{
if (Directory == null)
return new FileViewByFileName(FileName);
else
return new FileViewByDirectoryName(Directory, FileName);
}
else
return new FileViewByDirectoryNameCreateDirectoryOptional(Directory, FileName, CreatingDirectory);
}
}
Form:
public IFileViewerFactory FileViewerFactory { get; set; }
Button Click:
ILoader FileLoader = FileViewerFactory.GetFileViewer(TxtBoxFileName.Text);
Or:
ILoader FileLoader = FileViewerFacotry.GetFileViewer(TxtBoxFileName.Text,TxtBoxDirectory.Text);
So to finish off, my questions are:
Is my new way of "higher level" thinking, and bootstrapping from Program.cs now correct
How Can I handle optional parameters in LightInject
Is How I have setup my factory the correct way to do it?
Let's forget about the Fugliness of the factory and just try to work on the mechanics of the questions :)
I know it's a bit late to answer a question that is over a year old but let me try.
The issue here is that you don't want your container to go out anywhere else than your Composition Root. In a complex solution consisting of mutliple assembiles, this means the container itself is only referenced by the topmost assembly (where the Composition Root is).
But the application stack is usually complex, you possibly have multiple assembiles and still, your depencencies should be resolves across the application.
Historically, one of the possible approaches was the Service Locator pattern. The locator goes down to the very bottom of the stack and from there, it offers a service that resolves dependencies. Thus, it's available anywhere up the stack.
This approach has two drawbacks, first is that your container is referenced at the very bottom of the stack and even if you circuvment this you still have your locator referenced everywhere. The latter could be painful in a large app as you possibly have some standalone assembiles that you don't want to be forced to reference your locator (or anything else).
The ultimate solution is called the Local Factory (aka Dependency Resolver) and it takes care of creating just few of its dependand services. The trick is then to have multiple local factories across your app.
A typical setup is like this. Suppose there's an assembly, call it A, that the client will use to obtain an instance of IServiceA. The assembly contains only the two:
interface (obligation) of the service - IServiceA
the local factory clients will use to obtain instances of the service
And that's all, no other references, no containers. There's even no implementation at this point yet. The trick here is to make the factory open for the actual provider - in a sense that the factory doesn't even yet know how to create instances - it's the Composition Root that will tell it.
// Assembly A
public interface IServiceA
{
...
}
public class ServiceAFactory
{
private static Func<IServiceA> _provider;
public static void SetProvider( Func<IServiceA> provider )
{
_provider = provider;
}
public IServiceA Create()
{
return _provider();
}
}
the provider here has a functional contract but it could also be expressed as an interface
And that's all, although there's no implementation in the factory at the moment, the client code is suddenly very stable:
// client code to obtain IServiceA
var serviceA = new ServiceAFactory().Create();
Note again how self-contained this assembly A is. It has no other references, still, it offers a clean way to obtain instances of your service. Other assemblies can reference this assembly with no other additional references.
Then comes the Composition Root.
At the very top of your stack, your main assembly references the assembly A and some other assembly, let's call it AImpl that contains a possible implementation of the service interface.
technically the implementation of the service could be in the very same assembly the interface is but it only makes things easier
The Composition Root creates the provider of the factory by delegating a factory method down the stack, to the assembly A
// Composition Root in the top level module
// Both assemblies
// * A that contains IServiceA
// * AImpl that contains an implementation, ServiceAImpl
// are referenced here
public void CompositionRoot()
{
ServiceAFactory.SetProvider( () =>
{
return new ServiceAImpl();
} );
}
From now on, the provider is set up and all the client code down the stack that uses the factory, can succesfully obtain instances.
The Composition Root provides all other implementations of other local factories, as well. There are multiple setups then in the Composition Root:
SomeLocalFactoryFromAssemblyA.SetProvider( ... );
AnotherLocalFactoryFromAssemblyB.SetProvider( .... );
...
Where is your container then?
Well, the container is just one possible implementation of the provider. It only helps rather than spoils. Note however that you don't even have to use it, it's a choice rather than obligation.
public void CompositionRoot()
{
var container = new MyFavouriteContainer();
container.Register<IServiceA, ServiceAImpl>(); // create my registrations
ServiceAFactory.SetProvider( () =>
{
// this advanced provider uses the container
// this means the implementation, the ServiceAImpl,
// can have possible further dependencies that will be
// resolved by the container
container.Resolve<IServiceA>();
} );
}
This is the most clean setup I am aware of. It has all desired features:
it separates concerns in a clean way
the client doesn't really need any other dependencies than the service contract and the factory
the client doesn't even know there is or will be a container, in fact the client doesn't care
in a test environment, providers can easily be setup without any container, to provide static mocks of your services
the Composition Root is a real composer here - it's the only place in your code where the three: interfaces, implementations and the container, meet together

Any downfall (or better alternative) to run initialization code (HTTPHandler - URL association) using a static constructor?

Summary :
I have a DLL that hosts a class library. The library is used by an ASP.NET website. I need some code (initialization) to be run when the library is used. I have placed the code on the static constructor of one of the classes, which most likely will be used. It runs right now, but I was wondering
is there a better place to put this code? Some sort of DLL init
method?
are there any downfalls? If the class is never used, will the code
run anyways?
Details:
I have a DLL that hosts a class library that implements ECommerce to be used on ASP.NET websites. It contains controls and logic objects specific to my client. As part of it, it contains an HTTPhandler that handles AJAX calls to the library. The url that is associated with the Handler has to be registered. I have done this on the static constructor of one of the classes.
using System.Web.Routing;
class CMyClass {
static CMyClass() {
RouteTable.Routes.Insert(0, new Route("myapi/{*pathinfo}", new CMyHTTPHandlerRouter()));
}
}
This works right now. The site that uses the DLL does not have to register the route, which is very convenient. I was wondering, though:
is there a better place to register routes from a DLL? Or a better
way to associate a handler with a URL, directly from the DLL, so it
is always registered when the DLL is used.
are there any downfalls? If CMyClass is never used, will the code run anyways?
I can answer your second question: the static constructor will only run if you somehow interact with CMyClass. In other words, it's run on demand, not eagerly when you e.g. access the DLL.
Routes are to be construed as "application code". Meaning once it is "compiled" you cannot make changes to it. This is by design. Application_Start is the place where routes are normally registered.
I would normally abide by this convention. But my reusable logic (i.e. inside any publicly exposed method in the dll) should ensure that the routes are registered, else throw up an error. This is how the end developers know that they aren't using your component right. And if "it" knows the routes are registered it can safely go and execute the actual stuff.
I'd use a static boolean variable to accomplish that.
public class MyMvcSolution
{
public static bool Registered {get; set; }
static MyMvcSolution(){ Registered = false; }
public static void DoSomethingImportant()
{
if(Registered)
{
//do important stuff
}
else
throw new InvalidOperationException("Whoa, routes are not registered!");
}
//this should be called in the Application_Start
public static void Init()
{
RouteTable.Routes.Insert(0, new Route("myapi/{*pathinfo}", new CMyHTTPHandlerRouter()));
Registered = true;
}
}
I believe the above solution will kind of do.
There is an alternative strategy. We want to add routes "dynamically". This talks about forcing the BuildManager to register routes you mention is a .cs file. This file isn't "compiled" as part of the application; there will be a *.cs file in your application somewhere. You will make an assembly out of it on-the-fly, and from that force the buildmanager to register. There is also a mechanism to "edit" the routes once that file changes too. I'll leave it to you to explore this. Deep but interesting stuff.

Plugin Achitecture Extensibilty

I am working with MEF to get a plug in architecture going. I want to design in some extensibility. I want to extend initialization.
What I have is a "driver" which repetively collects data from some source. These are my plugins. Each of these plug ins needs to be initialized. Right now I have an interface that these plugins are required to implement.
interface IDriverLiveCollection
{
ILiveCollection GetCollection(ILog logger, IDriverConfig config);
}
This Interface is basically creating an instance of a ILiveCollection from the plugin. for better understanding ILiveCollection looks like this.
interface ILiveCollection
{
void GetData(Parameter param, DataWriter writer);
void ShutDown();
}
And also the initialization loop:
foreach(IDriverConfig config in DriverConfigs)
{
//Use MEF to load correct driver
var collector = this.DriverLoader(config.DriverId).GetCollection(new Logger, config);
// someTimer is an IObservable<Parameter> that triggers to tell when to collect data.
someTimer.Subscribe((param)=> collector.GetData(param, new DataWriter(param)));
}
The problem is that some driver may require more information than their configuration in order to initialize. For example, some driver would like a set of parameters given to them during initialization.
I could easily extend the interface to now look like:
interface IDriverLiveCollection
{
ILiveCollection GetCollection(ILog logger, IDriverConfig config, IEnumerable<Parameter> params);
}
The down side to this approach is that the public interface has changed and now i need to recompile EVERY driver even though none of the have needed this parameter list in order to function before. I intend on having ALOT of drivers and I will also not have any control over who writes drivers.
I thought up another solution. I could create interfaces and inside my loop between when I call Get Collection and before i subscribe to the timer, i could check if the ILiveCollection object also extends one of these interfaces:
interface InitWithParameters
{
void InitParams(IEnumerable<Parameter> params);
}
in my loop:
foreach(IDriverConfig config in DriverConfigs)
{
//Use MEF to load correct driver
var collector = this.DriverLoader(config.DriverId).GetCollection(new Logger, config);
// Check to see if this thing cares about params.
if(collector is InitWithParameters)
{
((InitWithparameters)collector).InitParams(ListOfParams);
}
// Continue with newly added interfaces.
// someTimer is an IObservable<Parameter> that triggers to tell when to collect data.
someTimer.Subscribe((param)=> collector.GetData(param, new DataWriter(param)));
}
The difference here is that I will not need to recompile every driver in order to get this to work. Old drivers will simply not be of type InitWithParameters and will not be called that way while new drivers will be able to take advantage of the new interface. If an old driver wants to take advantage, then it can simply implement that interface and be recompiled. The bottom line: I will not need to recompile drivers UNLESS they want the functionality.
The downsides that I have recognized are: I will obviously need to recompile which ever program is in this loop. There becomes a versioning issue when a new driver is used with an old version of the program with the loop which could result in some issues. and finally I have to hold a huge list of every possible type in the program with the loop as these things grow.
Is there a better way to do this?
Edit Additional Info:
I am attempting to use MEF on the IDriverLiveCollection, not on ILiveCollection since IDriverLiveCollection allows me to construct a specific ILiveCollection with custom initialization parameters.
It is possible to have 2 ILiveCollections of the same type (2 FooLiveCollections) each with a different ILog and IDriverConfig and potentially IEnumerable. I would like to be able to specify these during the "initialization loop" and not at the time of composition of the plugins.
If you just use [ImportMany] directly on your ILiveCollection interface, you could handle this entire infrastructure via the [ImportingConstructor] attribute.
This allows your plugins to specify exactly what they need to be able to be constructed, without having to provide and construct the types later.
Effectively, your host application would need nothing but:
// This gets all plugins
[ImportMany]
IEnumerable<ILiveCollection> LiveCollections { get; set; }
Each plugin would then have their type that exported this, ie:
[Export(typeof(ILiveCollection))]
public class FooLiveCollection : ILiveCollection
{
[ImportingConstructor]
public FooLiveCollection(ILog logger, IDriverConfig config)
{
// ...
Or, alternatively, a plugin could leave off one constructor argument, or add laters (without effecting previous plugins), ie:
[ImportingConstructor]
public BarLiveCollection(ILog logger, IDriverConfig config, IBarSpecificValue barParam)
{
// ...

AppDomain Unload killing Parent AppDomain

I am having trouble figuring something out about my AppDomain.Unload(...) call. I have a detailed explanation with code from my earlier question. As it turns out, I was performing a couple of steps that apparently, I don't need to. However, I am fairly certain that when an AppDomain is created and then held in a collection:
private static Dictionary<string , AppDomain> HostDomains;
void StartNewDomain(string domainName)
{
AppDomain domain = AppDomain.CreateDomain(domainName);
HostDomains[domainName] = domain;
}
...when you are done with it, you must unload it:
if (HostDomains.ContainsKey(domainName))
{
AppDomain.Unload(HostDomains[domainName]);
HostDomains.Remove(domainName);
}
then remove domain from the collection.
When I unload the domain, however, the entire application is ending. If I remove the unload, all is well...and we are simply left with removing the domain from the collection. But I fear that my child AppDomain is not really unloaded. It may eventually get GC'd I guess, but that doesn't give me a warm fuzzy.
The child AppDomain assembly (a Windows Form application) is started asynchronously via an interface (IModule) that is referenced in my adapter class which inherits MarshalByRefObject. I am wondering if this reference to IModule's Start() (which the plugin module assembly implements) is not marshaling properly (because of my implementation). So, when the Shutdown() method is called, the entire application dies. Should I make my IModule an abstract class instead so it should inherit MBR as well? Puzzled...
After looking at my code:
// instances the module for access to the module's Start() method
IModule module = (IModule)domain.CreateInstanceAndUnwrap(
ModuleManager.Modules[modName].Name,
ModuleManager.Modules[modName].EntryPoint.FullName);
...my fear is that since IModule is an interface, even though I am creating an instance in a child domain, the assembly is leaking into my main AppDomain. Therefore, when I attempt to unload the child domain, both domains are being unloaded. Would this be correct? And what would likely be the best solution to provide Start() & Stop() methods via the MBR (adapter) object?
UPDATE: see my answer below for changes --
Okay, there is no leaking -- everything inherits MBR:
Host : MarshalByRefObject -- instances the ModuleAdapter in a new AppDomain
ModuleAdapter : MarshalByRefObject -- IModule interface, interface methods (Start,Stop)
MyModulePlugin : MarshalByRefObject -- Application.Run(myForm)
Am I doing something wrong still? I've tried several things and it just seems to be wrong or incomplete. When I tell the ModuleAdapter to shutdown, it calls AppDomain.Unload(AppDomain.CurrentDomain) and the Host domain stops as well. I am still getting some first chance exceptions on the application exit. But the form (myForm) has been told to .Close().
So, I am still looking for the correct way of doing this...
As I suspected, instancing with the IModule interface in the primary domain causes a leak. In order to do this properly:
AppDomain domain = AppDomain.CreateDomain(domainName);
HostDomains[domainName] = domain; // put in collection
ModuleAdapter adapter = (ModuleAdapter)domain.CreateInstanceAndUnwrap(asmName , typeName);
where ModuleAdapter inherits MarshalByRefObject. Then:
adapter.Execute(moduleAssembly , moduleType);
Inside the ModuleAdapter class:
public void Execute(string Name, string EntryPoint)
{
module = (IModule)AppDomain.CurrentDomain.CreateInstanceAndUnwrap(Name , EntryPoint);
}
I do welcome comments or additional answers for a better way.
After moving the instancing to the ModuleAdapter class, we are still having the issue with AppDomain.Unload killing the entire application. I was wondering if this is because in the module plugin we are using Application.Run(myForm) - then when we shutdown we call myForm.Close(). Obviously this shuts down the application so I was wondering if the myForm.Close() also 'unloads' the AppDomain.

Categories

Resources