8

In my current framework's design, I peruse all types of specific assemblies and do work based on the types found. The assemblies it searches through are determined by the application bootstrapper/initializer. The assemblies are compiled in, so they can be strongly referenced via: typeof(SomeTypeInTheAssembly).Assembly. This is nice because the bootstrapper code has a strong reference to a type in the assembly and there's no fumbling about with fully qualified names as inline strings which need to be manually kept up-to-date if the assembly qualified name changes. But I didn't like referencing some completely unrelated type in the assembly and being dependant on that type being there. (what if it moves to another assembly? What if we deprecate/delete the type? Change its namespace?) In the code, it looks a bit weird too:

FrameworkAssemblyReader.Read(typeof(SomeAssemblyNamespace.SubNamespace.GraphingCalculator).Assembly);

Now in my bootstrapping code, I have a direct dependency (although a pretty trivial dependency) on GraphingCalculator which has nothing to do with the bootstrapping stage (and as a GraphingCalculator, it certainly has nothing to do with obtaining assembly references). To avoid this, in each assembly I intend to use in this way, I added a class in their root:

namespace SomeAssemblyNamespace
{
    public static class AssemblyReference
    {
        public static System.Reflection.Assembly Get
        {
            get
            {
                return typeof(AssemblyReference).Assembly;
            }
        }
    }
}

Which isn't too bad because then my bootstrapping code looks like:

FrameworkAssemblyReader.Read(SomeAssembly.AssemblyReference.Get);

But now, I have about a dozen or so assemblies with this AssemblyReference class copy/pasted and I only expect it to continue to grow as applications are built ontop of the framework. So my question is, is there a nice way to avoid the AssemblyReference class duplication and still programmatically pass in assembly references to the base framework, yet avoid the fuzziness of pointing to some arbitrary/unrelated type in the target assembly? My googlings seem to tell me that there's no API or language feature that lets me strongly point to an assembly (like typeof for classes), but I'm hoping someone here has come up with a better solution than I have that avoids the class/code duplication. Also of note, the code is sometimes compiled against Silverlight so I know that can tend to limit some of the API/techniques. Thanks!

EDIT: Just to add another monkey wrench, based on answer by "the coon", I should mention that I don't always load the same assemblies. For example, I might have a "CarBuilding" assembly that's applicable for some applications but not all. It's up to the bootstrappers to tell me which ones they wish to use (along with any custom ones the developer is employing)

EDITx2: To answer Jon Skeet's question: Clients (or us internally) will create any projects/DLLs they wish to support their application. In turn, these projects will reference the internal base framework. Their projects may contain resources (3D files, images, text, localization, etc.) and plugin-esque classes (they implement scripts which we discover and instantiate/execute as needed during the lifetime of the application). Additionally, our system supplies optional modules (DLLs) which contain reusable plugins/content which clients (or us) can leverage when making specific applications. So you might see a project structure like:

Solution
    ->MyAppBootstrapper_Android
        ->MyAppGUI_Android
        ->MyAppFramework
        ->MyAppResources
        ->MyAppAdditionalResources_Android

    ->MyAppBootstrapper_Silverlight
        ->MyAppGUI_Silverlight
        ->MyAppFramework
        ->MyAppResources
        ->MyAppAdditionalResources_Silverlight
        ->BaseFrameworkGraphingModule

The bootstrapper projects wire up dependencies and provide which projects/dlls (assemblies) should be provided to the base framework for discovery. Note that in this case, "MyApp" shares its own mini-framework and shared resources across both Android and Silverlight builds, but both have their own references independent from one another. The Silverlight one takes advantage of the BaseFrameworkGraphingModule and they have their own resources specific to the platform.

So in the bootstrappers in the projects look something like (albeit simplified):

namespace MyAppBootstrapper_Android
{
    public class Bootstrapper
    {
        public void Setup()
        {
            FrameworkAssemblyReader.Read(MyAppGUI_Android.AssemblyReference.Get);
            FrameworkAssemblyReader.Read(MyAppFramework.AssemblyReference.Get);
            FrameworkAssemblyReader.Read(MyAppResources.AssemblyReference.Get);
            FrameworkAssemblyReader.Read(MyAppAdditionalResources_Android.AssemblyReference.Get);
        }
    }
}

namespace MyAppBootstrapper_Silverlight
{
    public class Bootstrapper
    {
        public void Setup()
        {
            FrameworkAssemblyReader.Read(MyAppGUI_Silverlight.AssemblyReference.Get);
            FrameworkAssemblyReader.Read(MyAppFramework.AssemblyReference.Get);
            FrameworkAssemblyReader.Read(MyAppResources.AssemblyReference.Get);
            FrameworkAssemblyReader.Read(MyAppAdditionalResources_Android.AssemblyReference.Get);
            FrameworkAssemblyReader.Read(BaseFrameworkGraphingModule.AssemblyReference.Get);
        }
    }
}

So for each application built on top of the base framework, a bootstrapper is created that indicates which assemblies to include (and some other irrelevant wiring work). To answer the question specifically, the project has compile-time references to each assembly needed (this pulls double duty as it ensures that all DLLs are packaged together for Android/Silverlight deployment) as they are not dynamically downloaded to the user's smartphone or Silverlight app but packaged in the initial download. Your question as to how the Bootstrappers know which assemblies to use, well the developer knows and places necessary FrameworkAssemblyReader.Read calls. I hope this helps! Thanks for taking the time to look at it. I have a feeling I'm making too much out of nothing (or missing a far better solution/design altogether).

Finally, I neglected (stupidly) to mention that we also compile against Mono for Android, and in the near future, WPF. WinRT is a likely addition in the future but I'll be happy to cross that bridge when we come to it. Generally though, since each is compiled in their own way with their own platform features, I don't mind terribly if different platforms have a different way of pulling in the assembly references; though it would be nice if there was a unified syntax between the platforms.

EDITx3: Yeah, another one. I mentioned, but didn't clarify with an example that not all projects need or should be read by the base framework. For example, utility or business logic projects that have code that isn't relevant to the base framework but relevant to the client application. So from the above example:

Solution
    ->MyAppBootstrapper_Android
        ->MyAppGUI_Android
        ->MyAppFramework
        ->MyAppResources
        ->MyAppAdditionalResources_Android
        ->MyCompanySharedUtilities

    ->MyAppBootstrapper_Silverlight
        ->MyAppGUI_Silverlight
        ->MyAppFramework
        ->MyAppResources
        ->MyAppAdditionalResources_Silverlight
        ->BaseFrameworkGraphingModule
        ->MyCompanySharedUtilities

MyCompanySharedUtilities could be leveraged by the MyAppGUI_Silverlight, and MyAppFramework but the developer might not run it through FrameworkAssemblyReader.Read because it only contains, say, proprietary implementations of some math, or business rules.

Chris Sinclair
  • 22,858
  • 3
  • 52
  • 93
  • 2
    So do you always have compile-time references from *some* assembly (or assemblies) to all the relevant assemblies? How do the bootstrappers know which assemblies they want to use? – Jon Skeet Jul 03 '12 at 06:20
  • Thanks for the question @JonSkeet, I've added some additional information. – Chris Sinclair Jul 03 '12 at 12:33
  • 50 reputation points seems a unfair for the length of this question ;o) – Neil Knight Jul 03 '12 at 13:37
  • @NeilKnight It wasn't that long when I started. :) I can't figure out how to increase the bounty though; searching on `meta` seems to indicate I can't. I'll be happy to award the answer with additional reputation though. – Chris Sinclair Jul 03 '12 at 14:36
  • +1 for an interesting and thoroughly explained question. – JDB Jul 03 '12 at 16:51
  • 1
    Interesting. Your "solution" looks fine to me. If you like, you could use `Assembly.GetExecutingAssembly()` instead of `typeof(AssemblyReference).Assembly` inside your `AssemblyReference` class. You could also change the property to a `readonly` field if you want the `System.Reflection.Assembly` object to be created just once. – Jeppe Stig Nielsen Jul 03 '12 at 20:56
  • 1
    Thanks for the suggestion @JeppeStigNielsen. The `readonly` field with `GetExecutingAssembly` simplifies it a bit. I'll definitely switch to the `GetExecutingAssembly` method instead of `typeof(AssemblyReference).Assembly`. Still essentially duplicating the classes throughout each assembly, but it's a bit nicer. Thanks. If nothing comes up at the end of the open bounties, if you make it an answer I mark it. – Chris Sinclair Jul 03 '12 at 21:20
  • @JeppeStigNielsen: I implemented your suggestions into our software. It's only a minor syntax change, but it _feels_ nicer. I couldn't award you bounty so I upvoted a bunch of your answers. :) – Chris Sinclair Jul 06 '12 at 23:34
  • Thank you, I'm glad you found my comment useful. – Jeppe Stig Nielsen Jul 07 '12 at 09:34

1 Answers1

4

I'm not exactly sure how you expect your clients to use this code, so this suggestion may be irrelevant, but wouldn't it make more sense to do away with the Setup function (at least, hide it from the client) and use some kind of configuration file?

<ReferencedAssemblies>
    <ReferencedAssembly key="UnchangingKey" qualifiedName="SomeAssemblyName, version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456789abcdef" />
</ReferencedAssemblies>

Load the assemblies from the config file. Using a key, you don't have to update the application if the fully qualified name changes, just update the config file (and leave the key as-is).

This is, basically, the approach Visual Studio uses with its project files. Here's an example from one of my VB projects:

<ItemGroup>
  <Reference Include="System.Core">
    <RequiredTargetFramework>3.5</RequiredTargetFramework>
  </Reference>
  <Reference Include="System.Drawing" />
  <Reference Include="System.Windows.Forms" />
  <Reference Include="System.Xml.Linq">
    <RequiredTargetFramework>3.5</RequiredTargetFramework>
  </Reference>
  <Reference Include="System.Data.DataSetExtensions">
    <RequiredTargetFramework>3.5</RequiredTargetFramework>
  </Reference>
  <Reference Include="UIAutomationProvider">
    <RequiredTargetFramework>3.0</RequiredTargetFramework>
  </Reference>
  <Reference Include="WindowsBase">
    <RequiredTargetFramework>3.0</RequiredTargetFramework>
  </Reference>
  <Reference Include="PresentationCore">
    <RequiredTargetFramework>3.0</RequiredTargetFramework>
  </Reference>
  <Reference Include="PresentationFramework">
    <RequiredTargetFramework>3.0</RequiredTargetFramework>
  </Reference>
  <Reference Include="System" />
  <Reference Include="System.Data" />
  <Reference Include="System.Xml" />
</ItemGroup>

EDIT:

Here's a thought... what if you used Reflection.Emit to generate an assembly from the XML config file? You could add a stub class to the generated assembly which your core project could use to create a hard reference to the generated assembly, and you could use Reflection to probe each reference assembly (in the XML file) to create a hard reference to any object in the referenced assemblies. You'd need to regenerate the assembly when one of the referenced assemblies changes, but it would require no code changes. You could even build the assembly generation code into your core project so that users would be able to update their projects as needed.

JDB
  • 25,172
  • 5
  • 72
  • 123
  • Using XML as a resource _could_ work, but in my experience in the past, it's only ever lead to headaches. Furthermore, certainly in our Silverlight builds, it means that bootstrapper no longer has a strongly tied reference to the projects/assemblies. Visual Studio reports them as "unused references" and you can build the project without including them. But then the deployed XAP file no longer has the DLLs included which will naturally fail when trying to look them up by their full name. With the strong in-code references, everything is solid at compile-time with no runtime surprises. – Chris Sinclair Jul 03 '12 at 14:59
  • (cont.) I might resign to the fact that using an embedded config/xml to list which assemblies to include might be the generally accepted right way to go, but with our current processes and platforms it might be more pain than what it's worth. (which may indicate a problem elsewhere) Regardless, I'll give the answer a +1 and at consider a good way to feasibly incorporate it. – Chris Sinclair Jul 03 '12 at 15:01
  • One of the downsides of creating dynamic applications is that, be definition, your tools will not be able to support it properly. Whether you are dynamically loading an object using reflection (lack of strong typing) or dynamically loading an assembly (lack of strong references), you will have to do a lot of the groundwork yourself. I am not aware of any way around this requirement. Sorry. – JDB Jul 03 '12 at 15:37
  • Yeah, so right now we kinda have the best (worst?) of both worlds where clients or us internally have Visual Studio solutions built against our framework assemblies. So consumers of the framework have strong references where ever they're used. If a client wants to retrieve assembly references via XML, or downloaded, I guess that's their option. Internally, we're using the `AssemblyReference` class I posted, but we need to copy/duplicate it in each assembly we want to use. I was just hoping there'd be a nice code-equivalent/trickery like `typeof` but for assemblies. – Chris Sinclair Jul 03 '12 at 15:46
  • Not sure if I exactly understand. The idea is that somehow the developer would indicate what assemblies they want to use (via XML, attributes, build options, something or other), then we'd have a utility generate an assembly with a stub class that in turn passes the desired assemblies to our framework. Bootstrappers would hit a method in this stub-assembly to include it which does any wiring necessary? That might be something to consider; could even automate some of the bootstrapping wiring code (and some other internal stuff). Just let me know if I'm understanding you correctly. – Chris Sinclair Jul 06 '12 at 15:23
  • just forgot the notification marker for you. EDIT: Seems StackOverflow is stripping it out for some reason? :( – Chris Sinclair Jul 06 '12 at 15:45
  • Ahh, found it on meta: http://meta.stackexchange.com/questions/97098/eeeeek-what-happened-to-my-salutation (sorry for comment spam) – Chris Sinclair Jul 06 '12 at 15:53
  • You wouldn't want to generate the assembly at run-time since there would be no way to create a hard reference to the assembly (which must be done at compile time). But what you could do is move the Setup method into its own assembly which the core application has a hard reference to. The Setup assembly could be generated automatically from the XML file. This would at least remove the hard coding from the core application and make it easier to update (different Setup assemblies for different customers). – JDB Jul 06 '12 at 16:59
  • I think given my requirements, this would be the only way to pseudo-automate the process. At the very least, it gives me pause to think about some of our processes (about the framework automation, client consumption) that could be overhauled to use something like this. Thanks for the out-of-the-box thinking! – Chris Sinclair Jul 06 '12 at 23:17