tags:

views:

791

answers:

6

I have a solution with 2 projects.

One, Raven, is a simple base that provides data for the second project, PPather, to do stuff with. The second project depends on the first to compile so to build it, I add a reference to Raven. All works well so far.

Now I want Raven to launch PPather. But it can't see the PPather naemspace so I can't. All efforts to resolve this lead to circular reference errors.

Anyone know how I can get Raven to see the namespace of the PPather project that depends on it?

+9  A: 

You can't - there is no way to reference assemblies in a circular manner like you want to do. Most likely you have not properly designed these assemblies if you need to create a circular reference.

Your first assembly is a dependency so there should not be any code in there that knows about anything other than its dependencies. Once your assemblies become "smart" and begin to have knowledge of anything outside their own dependencies you will begin to have serious maintenance and scalability headaches. I would look into reorganizing your code in such a manner that you do not need to create the circular reference.

Andrew Hare
Agree with both sentences.
Colin Burnett
Couldn't agree more. Sounds like a nightmare. Reminds me of a bug in CMake that would lead to a circular dependency, so that CMake would hang in an endless loop...
OregonGhost
It seems I need to re-design. Many thanks for the fast clear answers!
Patrick
No problem - are there more details that you can post that we could use to help you sort it out?
Andrew Hare
Actually, that isn't quite correct. You *can* create circular references in .NET, but it is a: hard to do*, and b: a really, really bad idea. *=you can't do it in the IDE, for example - you have to drop to the command line. For a long while, 2 of the core MS .NET libs were circular (now fixed, I believe).
Marc Gravell
@Marc - very interesting - how does that work? I would be curious to know about it.
Andrew Hare
Basically, it involves using magic tools to generate special "skeleton" (that is, metadata only) assemblies that describe the shape of the types, but without any of their methods. Once you have generated code that can describe the metadata of both assemblies, then you can fully compile the method bodies of both assemblies, referencing the "skeleton" assemblies. It is a major pain in the rear and you should AVOID AVOID AVOID.
Eric Lippert
Wow - that sounds interesting and dangerous. How does that work when there are inherited types between the assemblies? Can you do this in instances where there are overriden members? I think it is fascinating that there are tools that allow you to separate a type's metadata from its implementation.
Andrew Hare
I've never tried those kinds of crazy scenarios and I hope that none of my coworkers have either. I don't know if the tools we've got support that or not.
Eric Lippert
A: 

There is a ton of stuff you can do to achieve this if you are not willing to combine them into one component. All basically strive to either invert one of the dependencies or to create a third component on which both depend.

It seems that Raven is the starting point, so one possible solution is to create a base class or interface in the PPather component which reflects the feature set that PPather seeks in Raven. Raven can then implement this base class and then include a "this"-pointer when instantiating/invoking PPather. PPather will expect a pointer to the base class (or interface) in his own assembly, and therefore will never "know of" Raven except through his own abstraction. Therefore, the circular dependency will be broken (by means of dependency injection).

Tormod
+1  A: 

As Andrew says, you can't and it doesn't make much sense that you'd want to.

Basically, do one of the following:

  • Merge the assemblies; if they really inter-depend tightly, then they really should not be separate in the first place.

  • Re-design the assemblies so that they do not directly depend on each other in both directions; for instance, make assembly A depend on an interface defined in assembly C, and have assembly B implement this interface (both depend on C).

jerryjvl
A: 

By Launching PPanther, do you mean that PPanther is an executable that you need to run?

In this case, you can use Process.Start to launch PPanther?

Patrick McDonald
A: 

It is fortunate that you can not add circular references - because they cause maintenance nightmares.

You want Raven to launch PPather? Is PPather as console/windows application? Use Process.Start to do that (and store the location of PPather in the registry somewhere).

Alternatively create interfaces for the classes that you need out of PPather - and make the classes in PPather implement those interfaces.

interface IPPatherInterface // Inside of Raven.
{
    void Foo();
}

class PPatherClass : IPPatherInterface // Inside of PPather
{
    // ...
}

class SomeRavenClass // Static maybe? Inside of Raven
{
    void SupplyPPatherClass(IPPatherInterface item) { ... }
}

You now have a way for PPather to supply that interface's implementation to Raven.

Jonathan C Dickinson
A: 

Branch out the calsses in raven that Panther needs to use from raven to a different assembly, and have both panther and Raven reference them.

Although to be honest if Raven needs to run panther then i think your design is a bit Off. you should break off your code into something more manageable.

Omar Kooheji