views:

40

answers:

2

We have a couple mini-applications (single Web form look-up stuff) that need to run in the context of a much larger site and application (hereafter "the Monolith")

We don't want to install the Monolith on every developer's machine, so we want some developers to be able to develop these little apps in their own isolated sandbox project (hereafter "Sandbox"). The idea is that we would move (1) the resulting DLL, and (2) the Web form (aspx) file from the Sandbox to the Monolithic Web App, where it would run.

This is all well-and-good, except that a couple of these little apps need to use a control that exists in the Monolith. And this control won't run without all the infrastruction of the Monolith behind it.

So, we had the great idea of creating a mock control. We stubbed out a control with the same namespace, class name, and properties as the control in the Monolith. We compiled this into a DLL, and put in in the Sandbox. We can develop against it, and it just spits out Lorem Ipsum-type data, which is cool.

The only reference to the control is this:

<Namespace:MyControl runat="server"/>

In the Sandbox, this invokes the mock object. In the Monolith, this invokes the actual control. We figured that since the only connection to the control is the tag above, it should work on both sides. At runtime, it would just invoke different things, depending on where it was running.

So we moved the aspx file and the app's DLL (not the mock object DLL) into the Monolith...and it didn't run.

It seems we didn't count on the "mypage.aspx.designer.cs" file coming out of Visual Studio. This gets compiled into the DLL, and it has a reference all the way back to our mock object DLL. So, when it runs in the Monolith, it complains that it can't load the mock object DLL.

So, our goal is to have the control tag as above, and have that invoke different things depending on the environment. In both cases, it would execute the same namespace and class, but that namespace and class would be different things between the two environments. In the Monolith it would be our actual control. In the Sandbox, it would be our mock object.

Essentially, we want this tag evaluated at runtime, not compile time. We want the DLL free of any reference back to the mock object's DLL.

Possible? Other solutions for the core problem?

A: 

I don't have a super quick fix for you (maybe someone else will) but you might be interested in a IoC container like Unity.

Gabriel
+1  A: 

This looks like a job for loose coupling! One of the most helpful, but least followed principles of software design is Dependency Inversion; classes should not depend directly on other concrete classes, but on abstractions such as an abstract base class or interface.

You had a great instinct with the mock object, but you implemented it incorrectly, if I understand right. You basically replaced a dependency on one concrete class (the Monolithic control) with another (the mocked control). The mock could have the same name, even the same namespace as the Monolith, but it resides in a different assembly than the Monolith control, which the dependent code expects because it was compiled to refer to that assembly, but does not find in the production environment.

I would start by creating an interface for your control, which defines the functionality available to consumers of the control (methods, properties, etc). Have both Monolith's control and the mocked control implement this interface, and declare usages of either control as being of the interface type instead of Monolith or the mock. Place this interface in a relatively lightweight DLL, seperate from the mocked object, the Sandbox and the existing Monolith DLLs, that you can place on developer's machines alongside the mocked object's DLL, and is also present in the Monolith codebase. Now, classes that need your control only really need the interface; you no longer need direct references to Monolith or to any mock DLL.

Now, when instantiating objects that are dependent on this control, you need some way of giving your new dependent object a concrete class that implements the interface, either the mock or the Monolith, instead of the dependent class creating a new one. This is called Dependency Injection, and is the natural extension of Dependency Inversion; specifying an interface is great, but if your dependent class has to know how to create a new instance of an object implementing that interface, you haven't gained anything. The logic for creating a concrete class must lie outside your dependent class.

So, define a third class that knows how to hook the control into classes that depend on it. The go-to method is to bring in an IoC framework, which acts as a big Factory for any object that either is a dependency, or has a dependency. You register one of the two controls as the implementation of the interface for a particular environment (the mock for dev boxes, the Monolith's control in production); the registration information is specific to each environment (usually located in the app.config). Then, instead of newing up an instance of the control class, you ask the container to give you an instance of a class that implements the interface, and out pops a new mock or Monolith control. Or, put the control and all dependent classes in the IoC container, and ask for the dependent classes, which the container will return fully "hydrated" with a reference to their control. All of this happens without any of the classes that depend on the control having to know where it came from, or even exactly what it is.

IoC frameworks can be a pain to integrate into an existing design, though. A workalike would be to create a Factory class that that uses reflection to dynamically instantiate either of the two controls, and place an AppSetting in the app.config file that will tell the factory which assembly and type to use for this environment. Then, wherever you'd normally new up a control, call the Factory instead.

KeithS
Keith: Thanks for the answer. Everything I've read indicates that this is the correct way to approach it. Sadly, the Monolith project is commercial, and we don't have access to the property source (the property is shipped with the product). We're looking at other options.
Deane