We have an app which optionally integrates with TFS, however as the integration is optional I obviously don't want to have all machine need the TFS assemblies as a requirement.
What should I do?
Alternatively the safer option would be to reference the TFS libraries in some separate "TFSWrapper" assembly:
a. Is it then ok for me to reference that assembly directly (again as long as I'm careful about what I call)
b. Should I instead be exposing a set of interfaces for my TFSWrapper assembly to implement, and then instantiate those objects using reflection when required.
1 seems risky to me, on the flip side 2b seems over-the-top - I would essentially be building a plug-in system.
Surely there must be a simpler way.
The safest way (i.e. the easiest way to not make a mistake in your application) might be as follows.
Make an interface which abstracts your use of TFS, for example:
interface ITfs
{
bool checkout(string filename);
}
Write a class which implements this interface using TFS:
class Tfs : ITfs
{
public bool checkout(string filename)
{
... code here which uses the TFS assembly ...
}
}
Write another class which implements this interface without using TFS:
class NoTfs : ITfs
{
public bool checkout(string filename)
{
//TFS not installed so checking out is impossible
return false;
}
}
Have a singleton somewhere:
static class TfsFactory
{
public static ITfs instance;
static TfsFactory()
{
... code here to set the instance
either to an instance of the Tfs class
or to an instance of the NoTfs class ...
}
}
Now there's only one place which needs to be careful (i.e. the TfsFactory constructor); the rest of your code can invoke the ITfs methods of your TfsFactory.instance without knowing whether TFS is installed.
To answer recent comments below:
According to my tests (I don't know whether this is 'defined behaviour') an exception is thrown when (as soon as) you call a method which depends on the missing assembly. Therefore it's important to encapsulate your code-which-depends-on-the-missing-assembly in at least a separate method (or a separate class) in your assembly.
For example, the following won't load if the Talk assembly is missing:
using System;
using OptionalLibrary;
namespace TestReferences
{
class MainClass
{
public static void Main(string[] args)
{
if (args.Length > 0 && args[0] == "1") {
Talk talk = new Talk();
Console.WriteLine(talk.sayHello() + " " + talk.sayWorld() + "!");
} else {
Console.WriteLine("2 Hello World!");
}
}
}
}
The following will load:
using System;
using OptionalLibrary;
namespace TestReferences
{
class MainClass
{
public static void Main(string[] args)
{
if (args.Length > 0 && args[0] == "1") {
foo();
} else {
Console.WriteLine("2 Hello World!");
}
}
static void foo()
{
Talk talk = new Talk();
Console.WriteLine(talk.sayHello() + " " + talk.sayWorld() + "!");
}
}
}
These are the test results (using MSVC# 2010 and .NET on Windows):
C:\github\TestReferences\TestReferences\TestReferences\bin\Debug>TestReferences.exe
2 Hello World!
C:\github\TestReferences\TestReferences\TestReferences\bin\Debug>TestReferences.exe 1
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'OptionalLibrary, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
at TestReferences.MainClass.foo()
at TestReferences.MainClass.Main(String[] args) in C:\github\TestReferences\TestReferences\TestReferences\Program.cs:
line 11
C:\github\TestReferences\TestReferences\TestReferences\bin\Debug>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With