Saturday, April 30, 2005

Contexts #2

One more iteration on the subject of contexts. But first, let me recap what I said so far: we have a certain type of variable marked as "contextual". When someone tries to read such a variable, the runtime checks to see if the variable's value has been set. If not, it finds the closest contextual parent that has this variable set, and uses it.

There could be two basic types of contextual parents: structural (i.e. the component has a pointer to a contextual parent) or caller (the calling method is the contextual parent). I've stated that we have to declare each contextual variable as a separate type, but it wasn't too clear what the declaration's scope would be. Let's clear this out and modify the philosphy a bit.

I tried to come up with a simple real-life example for contexts, but was unable to find one. While the usage of contexts could be broad (and there are already many concepts that are similar or equal to them - like ambient properties in windows controls, for example), they are mostly applicable in more complex situations. And complex situations don't make good examples.

One interesting application for contexts could be the Visual Studio .Net's design-time functionality. For those who aren't familiar with it, let's say that in Visual Studio's development environment the controls and components get instantiated the same way as in the runtime, but need to - and do - behave somewhat differently. Let's say we are developing a multi-tier application with such functionality in mind: we have a number of windows controls that communicate with the business logic layer, and the business logic communicates with the database-access layer. But, in design-time we want the database layer to behave like a stub component, i.e. do nothing. We signal that we're in design mode by setting to "true" the DesignTime contextual property on the windows form we're working on. (I know this works differently in Visual Studio, but let's pretend we're making an alternative system the way we want it).

Now, a database layer method needs to access this contextual property, and we definitely don't want to carry it around through method arguments. Furthermore, we don't want the value to be static because we want to turn on the database access when we need it (for example, we want to access the database from a list control to be able to automatically generate its columns).

It could work like this: Database layer classes would declare the DesignTime contextual variable as a calling-context type variable. Which means that they would inherit its value from the calling methods. The business logic layer classes would do likewise, and inherit the value from the controls that call them. Now, the controls have a hierarchical structure and their variable should be of the structural-context type. Thusly, a control would inherit the value from its parent controls, which would inherit the value from the form. Graphically, it could look something like this:



Note that the green arrows represent calling-context parent relations and the red represent structural parents. Comp1 and Comp2 are data-layer components.

So it looks like this is the solution: let each component declare its contextual variable and decide whether it will implement it using structural or calling hierarchy. Note that calling-context variables should probably behave like static properties, because they are not bound to objects.

Having all this in mind, we have several questions to answer:
  • Should we declare calling-context variables inside methods? They are obviously not bound to objects, but nevertheless there could exist an option to declare them at class level.
  • Could one class support both a structural hierarchy (at class level) and caller hierarchy (in some of its methods)?
  • Could a structural context be declared to use a static variable for parent context reference?
  • How do we know contextual variables on different classes have the same meaning? Only if their name is the same? Probably a separate contextual type needs to be declared?
  • Is there really no use here for .Net attributes, they look so cool?


Let's try to answer "yes" to all these questions, including the last (yes, there really is no use for attributes). Like this:

// declare a contextual variable type
public context bool DesignTime;

// a data layer class supporting this variable in a caller-
// context fashion
public class Comp2
{
protected DesignTime(Context.Caller);

public void AccessDatabase()
{
if(!DesignTime)
{
// access the database...
}
else
{
// just pretend you did
}
}
}

// a class with structural support
public class MyUserControl : UserControl
{
protected Control Parent;

// structural context - the structure is
// traced via the Parent property
protected DesignTime(Context.Structural,
Parent);
}

// a class with mixed caller, structural and
// static support
public class SomeClass
{
protected static object ContextualParent;

protected static DesignTime(Context.Structural,
ContextualParent);

public void SomeMethod()
{
DesignTime DesignTime(Context.Caller);

// ...
}
}


I haven't yet figured out a really elegant way to declare and consume contextual variables. Maybe in a later post...

Ok, now how about a more complex scenario? What happens if components in the data layer have a structural hierarchy, so there's more than one hierarchical path to choose from? Like this:



In this case, is it really good not to be able to dynamically determine whether structural or calling context would be used at one time? I think it probably is. If we need to have the same contextual property work differently at different times, I'm not sure but it may be a sign of bad design. I think it's cleaner to have the context variable's behavior decided statically. It seems simpler anyway.

Some additional notes:

  • These two context types (structural and caller) are probably not all there is, so there should be a way to declare one's own logic for them. The philosophy could possibly be borrowed from .Net's delegates (or whatever their equivalent in Java is).
  • What about components that don't declare support for the contextual variable? When traversing the hierarchy, they should simply be skipped. If a component acts as a crossroad from structural to calling hierarchy (like the DbControl in the illustration), it has to have the context variable declared. And to bring the clutter to a minimum, AOP should probably be called for help.
  • Could there be contextual methods? Possibly, yes, but they would need to behave differently than variables. While reading of a contextual variable triggers the search through hierarchy to the point where the variable's value has been set, accessing a contextual method would probably trigger a search to the point where the method has been implemented. Which probably also means the method would have to be a part of an interface.

Friday, April 15, 2005

Diagnosing .Net debugger problems after Office 2003 installation

I ran into this problem and was unable to find a good solution on the web: When you install Office 2003, the .Net debugger just stops working. Its startup time increases to a minute or two, and if you try to attach to a process for debugging you get a message that goes something like 'The debugger is not properly installed. Run setup to install or repair the debugger.'

There's a hint about this at blogs.msdn.com, but it didn't help in my case. If you have Visual Studio installed, running a repair setup will solve the problem without any fuss. (Of course, if you don't count disruption of your development process as fuss... Trust Microsoft to regularly come up with something that will drive your productivity down and make your nerves thinner). IIRC, after Visual Studio repair you also need to repair Office.

So: if you don't have Visual Studio, a reinstallation of .Net SDK and/or framework won't do the trick. What you have to do is delete the offending files an then repeat setup. I simply deleted the whole C:\Program Files\Common Files\Microsoft Shared\VS7Debug folder. Then I did a search on mscordbi.dll in the windows folder which produced two files (sbs_mscordbi.dll and mscordbi.dll) which I promptly deleted. Then I ran .Net SDK and .Net framework setups again, and that cracked it.

Now, you probably could get away with only deleting the VS7Debug folder, because the MDM.EXE file present inside it is clearly different after the repair installation. Before, it's version number was 11.something (yes, it reeks of Office all over), and afterwards it was 7.10, therefore it's the prime suspect. Also, I believe DbgClr.exe is present only in the SDK package and it could be sufficient to reinstall just the SDK. But, since DbgClr now works for me, I'll leave it to someone else to figure out the rest of the story :).