That being said, when working with AppDomain's, you have to make sure that the objects you're using can remote across to other AppDomain's correctly -- and by doing so, the easiest method is generally to derive the classes in the calls from MarshalByRefObject. Pretty easy, huh? Almost.
There is one caveat to this -- if you want to access an event on your cross-domain object, it requires your class to be Serializable or also be derived from MarshalByRefObject. Still pretty simple, right? Mostly -- but what if you have a control or user interface element that is already derived from something else? Something that shouldn't be serialized at all... In my case, it is a ViewModel.
After much deliberation, the smartest idea was to create a proxy -- pretty cool and easy, right? Almost. The only problem is that if I hard-coded the proxy, we'd be stuck with constantly updating the proxy for each and every object. Some of our calls are just a simple EventHandler<EventArgs>, but some of our calls are more complicated EventHandler<MakesYourMindExplodeFromAnotherAppDomainEventArgs>. How could I possibly allow this level of flexibility...?
Enter, the EventProxy:
public class EventProxy<T, Args> : MarshalByRefObject where Args : EventArgs { public event EventHandler<Args> EventOccurred; public EventProxy(T instance, string eventName) { EventInfo eventOccurred = typeof(T).GetEvent(eventName); if (eventOccurred != null) { MethodInfo onEventOccurred = this.GetType().GetMethod("OnEventOccurred", BindingFlags.NonPublic | BindingFlags.Instance); Delegate handler = Delegate.CreateDelegate(eventOccurred.EventHandlerType, this, onEventOccurred); eventOccurred.AddEventHandler(instance, handler); } } private void OnEventOccurred(object sender, Args e) { if (EventOccurred != null) EventOccurred.Invoke(sender, e); } }
It is incredibly simple.
It's also important to note that this needs to be an assembly that both AppDomains reference.
For my ViewModel's, I'm adding it as such:
EventProxy<CrossDomainObject, EventArgs<string>> Proxy = new EventProxy<CrossDomainObject, EventArgs<string>>(MyCrossDomainObject, "StatusUpdated");
Proxy.EventOccurred += (sender, e) => { Status = e.Value };
In this example, CrossDomainObject is my CrossDomainObject class, wherein MyCrossDomainObject is the instance of that class. The EventArgs<string> is a generic EventArgs that takes a string. From there, I just update my status in the ViewModel, without any issues of the SerializationException.
There are a few safe-guards that would help make sure the EventProxy doesn't blow up and Exception out to hell, but it's a good start for someone to build on top of.
No comments:
Post a Comment