Click here to Skip to main content
15,867,308 members
Please Sign up or sign in to vote.
4.83/5 (28 votes)
See more:
Hi all,
I spent my day reading articles/blogs about how to best share data between processes. I haven't had the opportunity to dive deep into this topic before, but I was wondering what the best solution would be in your opinion to handle the following.

I am developing an application that binds plugins using MEF. Plugins (mostly forms that are displayed as MDI forms) can have multiple interface implementations based on what they need/offer, the mainframe checks if they are implemented and then satisfies their needs.
So far everything is fine, up and running.

Now if you start the app it asks for a valid login (MainFrame) and if the login succeeds the user is stored in the MainFrame.
The PluginManager which knows about all the available plugins now passes the user into the plugin upon activation.

That works, but is it a good approach?

I also thought about having a AppEnvironment class that shares several information like User, DataConn-Strings etc. using static variables.
The App uses Tasks a lot so I secured the static variables that may be changed during runtime using static locking objects.

I already read here about that static variables are bad... Is this really the case?

Another thing that I figured out is that I could use pipes to share information between the MainFrame and its child-forms...
Isn't this a little bit overdosed? Do you like/use pipes for such requirements?

Don't get me wrong please, I am not searching for an easy way to archive what I want, but I really like to have a fine, elegant way to share the information within MainFrame and its ChildForms. If I can learn something new, even better ;)

Thanks for your attention, any help is kindly appreciated :)

cheers
Andy
Posted
Comments
Pablo Aliskevicius 8-May-12 10:19am    
Reason for my vote of 5
Great question!
hoernchenmeister 8-May-12 10:29am    
Thanks Pablo, I am already a little exited about how this will turn out ;)
Sandeep Mewara 8-May-12 10:53am    
5!
jim lahey 8-May-12 10:58am    
Reason for my vote of 5
Well-written question that deserves a good answer.
Roger Wright 8-May-12 11:12am    
Reason for my vote of 5
Excellent question, well phrased and detailed enough to be meaningful.

My first question is: what do you mean by 'process'? Is this really inter-process (in the OS sense) communication, or are you talking about interacting subsystems within the same application? The reason I ask is because plugins are usually implemented in the second way – in C#, usually by loading assemblies at runtime with Assembly.Load or Assembly.LoadFrom – and if you're actually using separate processes (Process.Start) I would need to know more about what you're doing to be sure that's the right approach.

If you're using genuine processes, you're limited to what the OS will give you; that's usually named pipes or sockets. I typically use TCP sockets because I can use classes I already wrote for networked communication, it makes it easy to split the system across a network if that's useful, and because that's what I have experience with. Any cross-process communication though will require you to serialise and deserialise transfer objects, you won't be able to share memory, etc.

I suspect you don't mean that, particularly with your references to static variables; I think you mean communicating between subsystems. In this case you can craft a solution that fits your exact needs. A good starting point is a message based architecture: modules can post messages on the queue of other modules (possibly just themselves and a controlling hub module, possibly any other module in the system; I don't know enough to judge on that), and modules process their message queues and do things. For example, a module that allows user entry could validate the data, process it with information that that module has available, and then post a message to the hub saying that data in table XYZ should be updated.

This has the advantage that you can stream data control messages serially, so you won't get race condition data corruption, but you don't have to make each module wait on all the others (which kills UI responsiveness). You can also have the hub tell other modules that data is updated so they should refresh their views (a bit like the Observer pattern but on a subsystem scale).
 
Share this answer
 
Comments
hoernchenmeister 8-May-12 11:12am    
Hi BobJanova,
first of all, thanks a lot for taking the time to reply to my question. It is very much appreciated!
You are totally right, I was talking about subsystems that interact with the application.
MEF (Managed Extensibility Framework) handles the activation of plugins for me, but more or less I am pretty sure it activates the instances using the Assembly namespace just as you described it.

The communication between the subsystems and the mainframe is handled using an event based model.
the subsystem raises an event that passes the message to the mainframe which then searches for plugins that can handle the message type that was sent. Upon finding a relevant plugin (they implement an ICanProcessMessage interface) the plugin is activated if not yet done and passes the message which is then handled by the plugin. After that the plugin manipulates the event arguments so that the sender knows what was done or not done.

I also have a MessageBus System at hand which is based on UDP that behaves like what you mentioned regarding the TCP sockets, but UDP is also something people say it is not the most elegant solution :)

Point is that it is a pretty early stage of the application and I try to be as flexible as possible, so crafting something that fullfills my needs is possible.

Regarding the static variables, I currently use them (as long as I don't know any better) to store information like the logged in user so that, theoratically all plugins could access the static ApplicationEnvironment class where the user is stored after the login (or a dummy user if no login was done due to plugin development). This class currently also provides conenction strings that are read from a Xml configuration (for the duck programmers) so that a plugin can use it.

That's where my quest started and where I read about how bad static variables are...
I was a little concerned just by reading that they are "bad practice", so I started to look around for something more elegant and extensible.

So there was little time until I stepped into named pipes. Due to the fact that I haven't used them before I am curious in learning new stuff, so personally I'd like to give them a chance and digg into it...

...but as you suerly know, a clear idea about something is the first step :)

Thanks a lot for your great reply, it already inspired me to go on and see what else can be done ;)

Cheers
Andy
I tried the pipe approach once, and it works fine if you have truly separate processes. As does shared memory, but that was in the days of C++. What I end up doing for what seems to be your need (plugins that don't actually run in separate processes, but maybe I'm wrong), I just provide a reference to an API that manages containers (bags, collections, whatever you want to call them.) I learned quickly that it's nice to have different containers, and I also learned that I want to abstract the "variable" (usually it's a native type, but it could be a complex type as well) so that I can trigger events when the value changes. It also is a good mechanism for doing some client-side validation: the value can't be null, it has to be within a certain range, etc., all of these constraints and validations can be tied in through the setter event.

So, in short, my solution requires an abstraction of memory management (the container) and an abstraction of a variable (aka memory location) as a full class citizen, the Var.

And quite frankly, if I have to work across processes, the concept still holds - the event getter/setter becomes a pipe push or pull, depending on what your needs are (but usually a push.)

More complicated things can be done as well by triggering an event when a set of information changes. For example, you only want to connect to a database when both username and password have been supplied (and maybe other information, like the database instance name, schema, and so forth.) If you go fully this route, it can literally change the way you code - it's pretty cool.

Marc
 
Share this answer
 
Comments
hoernchenmeister 8-May-12 11:24am    
Thanks Marc for your solution, I really appreciate the time you took to help me on this topic.
As you mentioned I didn't choose the right terminology for what my plugins are. They indeed do not run in seperate processes, but get activated by the mainframe just as you assumed.

I like the event approach you mentioned (and I already added a comment to solution1 in the meantime that explain a little more about the project I am working on - there is an event based messaging model available, ready for use).

Currently I am quite unsure how the app will evolve (the management and requirement bridge) but as you mentioned it might be necessary to work with different processes (real processes) in the future, so I will let you words run through my brain a little more.

Thanks again for your answer and for mentioning different ways/ideas, it is very much appreciated!!!

cheers
Andy
Gihan Liyanage 29-Aug-14 0:52am    
Great ....
Global variables are generally considered dangerous because all parts of the program can modify them at will so it becomes hard to be assured that their state is internally consistent and/or consistent with the state of the rest of the program at any given time.

There are two design considerations that come to mind immediately, that might help you create a workable, reliable and long-lived architecture:

1. If these 'global' values are initialise-once-but-thereafter-don't-change, you could make an object, with read-only (get-only) properties, that is implemented as a singleton, instantiated once its values have been obtained and initialised via constructor parameters.
This use of globals is safe because once initialised they never change ... and CAN NEVER change. That latter assurance is what makes it safe to let these values be global. They provide a (truly) static context for the rest of your program.

2. If, on the other hand, the variables you're considering are expected to be modified by the program, the do NOT constitute a static context and should not be global.
In this case, you may want to pass the from one subsystem to the next by value: this allows each subsystem to maintain a private copy of the values of these variables that were valid at they time the module received them.
If modules need to change the values of these variables you will need to consider how the rest of the program must behave when any one module modifies the value of one of these variables - probably with some kind of value-changed notification event.
Repeating the previous two sub-points:
a. pass-by-value: modules are insulated from changes to the variables in/by the rest of the program
b. pass-by-reference + change notification: can get tangled & hard to debug; needs a lot of thought; is little different from the use of globals (since all subsystems have a reference to the actual value).
 
Share this answer
 
Comments
hoernchenmeister 8-May-12 11:34am    
Hi Chris,
thanks a lot for taking the time to share your knowledge, I really appreciate that.

The ApplicationEnvironment class I mentioned somehow fits into what you have described.
It is a singleton that is initialized with a valid user via its factory methods after login.
After that the variables I currently think of are very unlikely to change during the apps lifetime (logged in user, connections etc.)

I try to assume what requirements will come up in the near future, but as you might have already experienced, requirements change if management decides that it has to be done ;)
So unfortunately there might be a risk that the truely static context will be softened due to "certain influences" that I am not capable of right now.

Thanks again for sharing your ideas/thougths with me, it already helped to clear the fog around me ;)

cheers
Andy
Based on your reply to BobJanova, it sounds like what you are actually looking for is for a mechanism to support a MEF based architecture to pass messages between your MEF objects. This is a fairly common requirement in disconnected systems, and is one that is often brought up/thought about in the WPF/Silverlight world where the MVVM pattern emphasises the disconnected nature of the different components of the system.

Effectively, in this type of system, two techniques have become fairly common. The first is to use an interface and tie it up to a service implementation - this would be loaded in by MEF, and the classes that are interested in it can subscribe to events on it to receive notifications, or they can act on methods on the service to trigger the notifications.

The second, and much commoner technique in the MVVM world, is to implement something known as a Mediator. Basically, this is a pattern whereby a plugin registers it's interest in one or more well defined messages (along with an action to be triggered on receipt of the notification). A component triggers the Mediator to notify the colleagues (plugins) of a particular message, and the Mediator looks up those instances that are interested in the notification and it triggers an action on the plugin.

Whichever technique you end up choosing, careful management is needed to ensure that the lifetime of "interested" components is managed carefully, otherwise you end up with an application that leaks memory.
 
Share this answer
 
Comments
hoernchenmeister 8-May-12 11:50am    
Hi Pete,
thanks a lot for your answer and for sharing your thoughts on this topic.

I guess there are two parts that I identified till now reading through all the answers.

The first is the notification part.
I described what I have in my reply to BobJanova's answer, but thats just something I play around with. It would definitely profit from your description on subscribing to certain events. Currently it passes messages from a plugin to another plugins (via mainframe) that tell the mainframe that it can handle message ABC.

The second part is what "Chriss Ross 2" mentioned beeing a "truely static context"
So if the user once logged in it will never change and might be handled by what I called ApplicationEnvironment class so far.
Even if those variables will never change after initialization I would prefer a way to combine both under a single hood.

I will read about the MVVM Mediator pattern you mentioned, unfortunately my knowledge regaring this topic is lacking details :)

Thanks again for your contribution, it is very much appreciated and you already inspired me to think into another direction I didn't thought of before :)

cheers
Andy
iopiopiopopiGlobal variables are generally considered dangerous because all parts of the program can modify them at will so it becomes hard to be assured that their state is internally consistent and/or consistent with the state of the rest of the program at any given time.

There are two design considerations that come to mind immediately, that might help you create a workable, reliable and long-lived architecture:

1. If these 'global' values are initialise-once-but-thereafter-don't-change, you could make an object, with read-only (get-only) properties, that is implemented as a singleton, instantiated once its values have been obtained and initialised via constructor parameters.
This use of globals is safe because once initialised they never change ... and CAN NEVER change. That latter assurance is what makes it safe to let these values be global. They provide a (truly) static context for the rest of your program.

2. If, on the other hand, the variables you're considering are expected to be modified by the program, the do NOT constitute a static context and should not be global.
In this case, you may want to pass the from one subsystem to the next by value: this allows each subsystem to maintain a private copy of the values of these variables that were valid at they time the module received them.
If modules need to change the values of these variables you will need to consider how the rest of the program must behave when any one module modifies the value of one of these variables - probably with some kind of value-changed notification event.
Repeating the previous two sub-points:
a. pass-by-value: modules are insulated from changes to the variables in/by the rest of the program
b. pass-by-reference + change notification: can get tangled & hard to debug; needs a lot of thought; is little different from the use of globals (since all subsystems have a reference to the actual value).
 
Share this answer
 
Comments
hoernchenmeister 27-Jul-12 5:35am    
Thanks for your nice explanation Cloud2.0, it is very much appreciated.
I like the concepts you introduce in point 2, because I had indeed several occasions and different situations where I applied what I learned above.
The "truely static" ones following a singleton where the least troubeling ones, but most of them really changed values over their lifetime.
In the meantime I read some articles about remoting/pipes and their mechanisms to pass values byval/byref. Even if the byval approach seems to be a lot easier it becomes unusable very quickly because of the requirements that tend to evolve :)
On the other hand, as you have already mentioned, the byref approach becomes complicated very quickly :)

Thanks again for your contribution,
have a great day
Andy

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900