we have a method which takes the folder name and number of days as a parameter.
public void Delete(string folder, int days)
{
var files = Directory.GetFiles(folder);
foreach (var file in files)
{
var fi = new FileInfo(file);
var fiCreationTime = fi.CreationTime;
var deleteOlderThan= DateTime.Now.AddDays(-days);
if (fiCreationTime >= deleteOlderThan) continue;
fi.Delete();
}
}
What is the best way to unit test such methods in c#
Actually you cannot unit test your method, because it depends on external APIs (FileSystem, DateTime).
So what you should do is separate logic and integration with external sources, it might look like this:
public class MyFileInfo
{
public string FileName { get; set; }
public DateTime CreationTime { get; set; }
}
public interface IDateTimeProvider
{
DateTime GetCurrentTime();
}
public interface IMyFileSystemService
{
IEnumerable<MyFileInfo> GetFileInfos(string folder);
void DeleteFile(MyFileInfo myFileInfo);
}
public class MyService
{
private readonly IMyFileSystemService _myFileSystemService;
private readonly IDateTimeProvider _dateTimeProvider;
public MyService(IMyFileSystemService myFileSystemService, IDateTimeProvider dateTimeProvider)
{
_myFileSystemService = myFileSystemService;
_dateTimeProvider = dateTimeProvider;
}
public void Delete(string folder, int days)
{
var files = _myFileSystemService.GetFileInfos(folder);
foreach (var file in files)
{
var deleteOlderThan = _dateTimeProvider.GetCurrentTime().AddDays(-days);
if (file.CreationTime >= deleteOlderThan) continue;
_myFileSystemService.DeleteFile(file);
}
}
}
I think implementation of interfaces IDateTimeProvider and IMyFileSystemService should not be an issue.
Now you can write clean unit tests for MyService.Delete
If you want to unit test the method you have shown it would be difficult in the way it is currently written, but we could mock the File class and pass an interface for it to use:
public class FileDeleter
{
private readonly IFileOperator _fileOperator;
public FileDeleter(IFileOperator fileOperator)
{
_fileOperator= fileOperator
}
public void Delete(string folder, int days)
{
var files = _fileClass.GetFiles(folder);
foreach (var file in files)
{
var fi = _fileClass.GetFileInfo(file);
var fiCreationTime = fi.CreationTime;
var deleteOlderThan= DateTime.Now.AddDays(-days);
if (fiCreationTime >= deleteOlderThan)
continue;
fi.Delete();
}
}
}
public interface IFileClass
{
IEnumerable<string> GetFiles(string path);
IFileInfo GetFileInfo(string filePath);
}
public interface IFileInfo
{
DateTime CreationTime { get; }
void Delete();
}
After that, simply mock the two classes with a library like: https://github.com/Moq/moq4/wiki/Quickstart
and write your unit tests testing whatever logic is required.
EDIT: As others have pointed out, datetime.now might be a good thing to mock too, but can be done in that same way.
Another possibility is to use static helper classes
public static class FileEx
{
public static Func<string, IEnumerable<string>> EnumerateFiles { set; get; }
= Directory.EnumerateFiles;
}
and then only use the helper classes:
var files = FileEx.EnumerateFiles(...);
This way you can change the method in your unit tests.
[Test]
public void Test()
{
FileEx.EnumerateFiles = (_) => new [] { "file1", "file2" };
// your test here
// Reset the method:
FileEx.EnumerateFiles = Directory.EnumerateFiles;
}
This works for most static helper methods and is way easier refactoring every class so it can be injected.
Downsides
you will lose function overloading.
will only work for static classes (in your example it won't work for FileInfo).
Upsides
really easy
easy to implement
easy to change while testing
easy to use
Update to remarks in the comments:
It is viable to replace system methods as Directory.EnumerateFiles in your unit tests.
Because you are testing your Delete method and one can assume that Microsoft has tested the framework code. Therefore the only thing the unit test must prove is that the Delete method has to correct output and side effects.
The 100% solution is that lot of this has to be injected in because it either has sideeffects or is non-determenistic:
1) Directory.GetFiles
2) new FileInfo(file)
3) fi.CreationTime
4) DateTime.Now.AddDays
5) fi.Delete
So you inject e.g. a datetimeservice in that in production that returns datetime, and in test where it always returns some fixed date. And use a mocking framework to check that sometimes the delete method is called, and other times it isn't called.
The perfect solution for your code would create an interface which has methods for all files operation and then mock those methods
but you can also create virtual methods for those file operation in your sample class and mock that method in a unit test
Following is code implementation of your actual code
public class Sample
{
public void Delete(string folder, int days)
{
var files = GetFiles(folder);
foreach (var file in files)
{
var fi = GetFileInfo(file);
var fiCreationTime = fi.CreationTime;
var deleteOlderThan = DateTime.Now.AddDays(-days);
if (fiCreationTime >= deleteOlderThan) continue;
DeleteFile(fi);
}
}
public virtual void DeleteFile(FileInfo f)
{
f.Delete();
}
public virtual string[] GetFiles(string path)
{
return Directory.GetFiles(path);
}
public virtual FileInfo GetFileInfo(string file)
{
return new FileInfo(file);
}
}
And following is your unit test class
public class NUnitTest
{
[TestFixture]
public class UnitTest1
{
private Mock<Sample> _sample;
private FileInfo _fileInfo;
[SetUp]
public void Setup()
{
_sample = new Mock<Sample>();
}
[Test]
public void File_Should_Not_Delete()
{
_fileInfo = new FileInfo("file");
_fileInfo.Create();
_sample.Setup(x => x.GetFiles(It.IsAny<string>())).Returns(() => new[] {"file1"});
_sample.Setup(x => x.GetFileInfo(It.IsAny<string>())).Returns(() => _fileInfo);
_sample.Setup(x => x.DeleteFile(It.IsAny<FileInfo>())).Verifiable();
_sample.Object.Delete("file1",2);
_sample.Verify(x => x.DeleteFile(It.IsAny<FileInfo>()), Times.Never);
}
[Test]
public void File_Should_Delete()
{
_fileInfo = new FileInfo("file1");
_fileInfo.Create();
_sample.Setup(x => x.GetFiles(It.IsAny<string>())).Returns(() => new[] { "file1" });
_sample.Setup(x => x.GetFileInfo(It.IsAny<string>())).Returns(() => _fileInfo);
_sample.Setup(x => x.DeleteFile(It.IsAny<FileInfo>())).Verifiable();
_sample.Object.Delete("file1", -2);
_sample.Verify(x => x.DeleteFile(It.IsAny<FileInfo>()), Times.Once);
}
}
}
I know this is not good design practice but I just want to showcase you a different way of doing unit testing using a virtual method.
Bestway is to create an interface and mock those interface method which I created as a virtual
Related
I'm trying to customize some of nUnits behaviour, however I'm constantly hitting a brick wall
because of nUnits heavy use of code reflection. Test methods (and also setup methods etc) are passed all the way down, deep into the framework, and are converted into a delegate at the latest step possible.
The classes I'm interested in are called TestCommands and only there the framework becomes functional.
For reference here is a snippet I found in nUnits source of the TestMethodCommand class which propably is the bread and butter test execution delegate.
public class TestMethodCommand : TestCommand
{
private readonly TestMethod testMethod;
private readonly object[] arguments;
public TestMethodCommand(TestMethod testMethod) : base(testMethod)
{
this.testMethod = testMethod;
this.arguments = testMethod.Arguments;
}
public override TestResult Execute(TestExecutionContext context)
{
object result = InvokeTestMethod(context); // missing a branch deciding about sync vs. async
// missing some code that checks object against "expected result"
return context.CurrentResult;
}
private object InvokeTestMethod(TestExecutionContext context)
{
return testMethod.Method.Invoke(context.TestObject, arguments);
}
}
I'm puzzled why nUnit couldn't wrap the test method into an Func<object> way way sooner and just pass the context along. As it stands for now if I don't have a MethodInfo nUnit can't run it.
In case you wonder, here is an example of a thing I want to do but I ran into the same problem in other instances as well.
[Scenario(When: "Device Registration reads out PCB Type",
Then: "Device Type might change")]
public void Identifier_Changes_Are_Recognized()
{
var changedType = reference.ChangeType(DeviceType.Terminal);
var changedID = reference.ChangeID(123456);
Assert.Multiple(() =>
{
AssertIsSameDevice(reference, changedType);
AssertIsDifferentDevice(reference, changedID);
});
}
This scenario attribute is supposed to print a small description like so.
public void RunBeforeTest()
{
var text = new MultiLineText
("Scenario:",
"\tGiven:\t" + When,
"\tThen:\t" + Then,
"-------------\n"
);
Console.WriteLine(text);
}
I reaaallly want to tell nUnit "Look, here is an action, please run it" but for the time beeing this seems very hard to achieve. Did anyone else here run in these kinds of problems?
Are there possibly ways to achieve what I'm trying to do? Maybe create my own TestCommand, but as I mentioned, these objects only get created very deep into the framework.
OP here (Writing from my home account)
I looked more into this and actually found a working solution:
public class ArbitraryCodeExecutionWrapper : DelegatingTestCommand
{
public ArbitraryCodeExecutionWrapper(TestCommand innerCommand) : base(innerCommand)
{
}
public Action<TestExecutionContext> BeforeTest { get; init; } = _ => { };
public Action<Test, TestResult> AfterTest { get; init; } = (_,_) => { };
public override TestResult Execute(TestExecutionContext context)
{
BeforeTest(context);
var result = innerCommand.Execute(context);
AfterTest(context.CurrentTest, result);
return result;
}
}
public class NUnitTestCommandWrapperAttribute : Attribute, IWrapTestMethod
{
protected virtual void BeforeTest(TestExecutionContext context)
{
}
protected virtual void AfterTest(Test test, TestResult result)
{
}
public TestCommand Wrap(TestCommand command)
=> new ArbitraryCodeExecutionWrapper(command)
{
BeforeTest = BeforeTest,
AfterTest = AfterTest
};
}
public class ScenarioAttribute : NUnitTestCommandWrapperAttribute
{
public string When { get; init; } = "";
public string Then { get; init; } = "";
protected override void BeforeTest(TestExecutionContext context)
{
var text = new MultiLineText
("Scenario:",
"\tGiven:\t" + When,
"\tThen:\t" + Then
);
Console.WriteLine(text);
}
protected override void AfterTest(Test test, TestResult result)
{
Console.WriteLine("After Test");
}
}
[TestFixture]
public class TestCodeExecution
{
[Test]
[Scenario(When = "nUnit Comes here",
Then = "Print Hello World")]
public void Try_Out_Code_Execution()
{
Console.WriteLine("Hello World");
}
}
public class MultiLineText
{
private List<string> items = new();
public static implicit operator string(MultiLineText text) => text.ToString();
public MultiLineText(params string[] lines)
{
items = lines.ToList();
}
public override string ToString() => string.Join("\n", items);
}
My unit testing method is as follows
[Test]
public void TrackPublicationChangesOnCDSTest()
{
//Arrange
// objDiskDeliveryBO = new DiskDeliveryBO();
//Act
var actualResult = objDiskDeliveryBO.TrackPublicationChangesOnCDS();
//Assert
var expectedZipName = 0;
Assert.AreEqual(expectedZipName, actualResult);
}
The Actual method TrackPublicationChangesOnCDS in BO is as follows
public int TrackPublicationChangesOnCDS()
{
var resultFlag = -1;
try
{
string pubUpdateFileCDSPath = CommonCalls.PubUpdateFileCDSPath;
string pubUpdateFileLocalPath = CommonCalls.PubUpdateFileLocalPath;
if (File.Exists(pubUpdateFileCDSPath))
File.Copy(pubUpdateFileCDSPath, pubUpdateFileLocalPath, true);
if (File.Exists(pubUpdateFileLocalPath))
{
string[] pubRecords = File.ReadAllLines(pubUpdateFileLocalPath);
var pubRecordsExceptToday = pubRecords.Where(p => !p.Trim().EndsWith(DateTime.Now.ToString("dd/MM/yy"))).ToList();
resultFlag = new DiskDeliveryDAO().TrackPublicationChangesOnCDS(pubRecordsExceptToday);
File.WriteAllText(pubUpdateFileLocalPath, string.Empty);
string[] pubRecordsCDS = File.ReadAllLines(pubUpdateFileCDSPath);
var pubRecordsTodayCDS = pubRecordsCDS.Where(p => p.Trim().EndsWith(DateTime.Now.ToString("dd/MM/yy"))).ToList();
File.WriteAllLines(pubUpdateFileCDSPath, pubRecordsTodayCDS);
}
return resultFlag;
}
catch (Exception)
{
return -1;
}
}
While debugging Debugger comes till
string pubUpdateFileCDSPath = CommonCalls.PubUpdateFileCDSPath;
But CommonCalls.PubUpdateFileCDSPath; return empty string . It should return a file path . when the method is called directly it works fine . It doesn't work when it is called inside a unit testing method.
CommonCalls.PubUpdateFileCDSPath is a static property defined as below .
public static string PubUpdateFileCDSPath
{
get { return GetXmlConfigValue("PubUpdateFileCDSPath"); }
}
public static string GetXmlConfigValue(string nodeName)
{
var xml = new XmlDocument();
xml.Load(ConfigValuesXml);
var node = xml.SelectSingleNode("JanesOfflineDeliveryService/" + nodeName);
return node != null ? node.InnerText : string.Empty;
}
Configvaluesxml is a xml file path . Contents of the file is
<JanesOfflineDeliveryService>
<PubUpdateFileCDSPath>D:\OfflineDelivery\CDS\pub_update.txt</PubUpdateFileCDSPath>
<PubUpdateFileLocalPath>D:\pub_update.txt</PubUpdateFileLocalPath>
</JanesOfflineDeliveryService>
In your test scenario GetXmlConfigValue("PubUpdateFileCDSPath") does not exist, so string empty is returned. Thats why you should avoid static methods, because they are not mockable. A workaround could be to pass the path variables into the method.
Using static dependencies make unit testing code in isolation difficult. invert the dependency by abstracting and injecting them into the dependent class.
public interface ICommonCalls {
string PubUpdateFileCDSPath { get; }
string PubUpdateFileLocalPath { get; }
}
the implementation of the above interface would either wrap the your static calls or better yet just implement them.
The dependent class would be refactored to allow for the dependency inversion.
public class DiskDeliveryBO {
private readonly ICommonCalls CommonCalls;
public DiskDeliveryBO(ICommonCalls common) {
this.CommonCalls = common;
}
//...other code removed for brevity.
}
However the target method also has a lot of tight coupling to implementation concerns like the file system. That too should be abstracted and inverted out of the dependent class.
I have the followed function and trying to add Unit Test on a old project. I'm a beginner in Unit Test so forgive me if the question is stupid ...
public static string GetDefaultName(bool isResponsive)
{
//Read web.config file
Configuration configuration = WebConfigurationManager.OpenWebConfiguration(System.Web.HttpContext.Current.Request.ApplicationPath);
if (!isResponsive)
{
if (configuration.AppSettings.Settings.AllKeys.Contains("defaultTheme"))
{
return configuration.AppSettings.Settings["defaultTheme"].Value;
}
else
return "default";
}
else
{
// ...
}
}
And I'm trying to write an Unit Test in this way :
[TestMethod]
public void ReturnDefaulThemeNametIfThemeIsResponsive()
{
var theme = new Theme {isResponsive = true};
var defaultName = Themes.GetDefaultName(theme.isResponsive);
Assert.AreEqual(defaultName, "defaultThemeResponsive");
}
I wonder what is the best way to test this static function, and how to mock the part who read the web.config file ?
I try to stay away from static utilities that have dependencies as they are difficult to unit test. But in this case it is possible. You will have to do some refactoring.
First you need to abstract all calls to access configuration.
public interface IThemeSettings {
bool Contains(string key);
string this[string key] { get; }
}
You can then update the static Themes utility class to use this abstraction as a dependency
public static class Themes {
private static IThemeSettings themes;
public static void Configure(Func<IThemeSettings> factory) {
if (factory == null) throw new InvalidOperationException("Must provide a valid factory method");
themes = factory();
}
public static string GetDefaultName(bool isResponsive) {
if (themes == null) throw new InvalidOperationException("Themes has not been configured.");
string result = string.Empty;
if (!isResponsive) {
if (themes.Contains("defaultTheme")) {
result = themes["defaultTheme"];
} else
result = "default";
} else {
// ...
}
return result;
}
//...
}
That wat you can now configure the utility to use mocks when testing
[TestMethod]
public void ReturnDefaulThemeNametIfThemeIsResponsive() {
//Arrange
var key = "defaultTheme";
var expected = "defaultThemeResponsive";
var mockSettings = new Mock<IThemeSettings>();
mockSettings.Setup(m => m.Contains(key)).Returns(true);
mockSettings.Setup(m => m[key]).Returns(expected);
//In production you would also do something like this with
//the actual production implementation, not a mock
Themes.Configure(() => mockSettings.Object);
var theme = new Theme { isResponsive = true };
//Act
var defaultName = Themes.GetDefaultName(theme.isResponsive);
//Assert
Assert.AreEqual(expected, defaultName);
}
In this case I used Moq as the mocking framework.
Some advice. Try not to have your classes tightly coupled to HttpContext. Your classes should depend on abstractions and not on concretions.
The way your method is designed at the moment does not allow you to mock the part that reads the config file. If you want to be able to do that you need to make it a parameter to your method. One way to make that easier is to define an interface like
public interface ISetting
{
string GetConfigItem(string itemName);
}
Then wrap the Configuration object in a settings manager class that implements this.
public class MySettings:ISetting
{
public string GetConfigItem(string ItemName)
{
// return value of the setting. In your case code that gets value of "defaultTheme"
}
}
Your method will now have a dependency on ISetting.
For testing purposes you can create a mock that implements the interface and will return what ever value you want independent of the current state and content of the web.config
public class SettingsTestHelper:ISetting
{
private _valueToReturn;
public SettingsTestHelper(string valueToReturn)
{
_valueToReturn=valueToReturn;
}
public string GetConfigItem(string itemName)
{
return valueToReturn;
}
}
With this you can now create a unit test(doesn't compile, but you'll get the idea)
[TestMethod]
public void CanGetSetting()
{
var helper = new SettingsTestHelper("default");
var result = ClasThatImplementsYourStaticMethod.GetDefaultName(helper, true);
Assert.AreEqual(expected, actual);
}
I have the following class
public interface IAuthProvider
{
string GenerateKey();
}
public class AuthProvider : IAuthProvider
{
public string GenerateKey()
{
using (var rng = new RNGCryptoServiceProvider())
{
var data = new byte[16];
rng.GetBytes(data);
return BitConverter.ToString(data).Replace("-","");
}
}
}
I also have the follow unit tests to go with it
[TestClass]
public class AuthProviderTests
{
private AuthProvider _provider;
private string _key;
[TestInitialize]
public void Initialize()
{
_provider = new AuthProvider();
_key = _provider.GenerateKey();
}
[TestMethod]
public void GenerateKey_key_length_is_32_characters()
{
Assert.AreEqual(32, _key.Length);
}
[TestMethod]
public void GenerateKey_key_is_valid_uppercase_hexidecimal_string()
{
Assert.IsTrue(_key.All(c =>
(c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'F')
));
}
[TestMethod]
public void GenerateKey_keys_are_random()
{
var keys = new List<string>
{
_provider.GenerateKey(),
_provider.GenerateKey(),
_provider.GenerateKey(),
_provider.GenerateKey(),
_provider.GenerateKey()
};
var distinctCount = keys.Distinct().Count();
Assert.AreEqual(5, distinctCount);
}
}
Everything works great. However I need to create a method (and tests to go with it) called GenerateSecret. This method will do exactly the same as GenerateKey().
Now I am thinking I should create a method called GenerateRandomHexString(int bytes) and copy the code from GenerateKey into it. Then for GenerateKey and GenerateSecret I should use the follow code:
public interface IAuthProvider
{
string GenerateKey();
string GenerateSecret();
string GenerateRandomHexString(int bytes);
}
public class AuthProvider : IAuthProvider
{
public string GenerateKey()
{
return GenerateRandomHexString(16);
}
public string GenerateSecret()
{
return GenerateRandomHexString(16);
}
public string GenerateRandomHexString(int bytes)
{
using (var rng = new RNGCryptoServiceProvider())
{
var data = new byte[bytes];
rng.GetBytes(data);
return BitConverter.ToString(data).Replace("-","");
}
}
}
Now for the tests, should I just write the tests for the GenerateRandomHexString method, or should I write tests also for the GenerateSecret and GenerateKey (which will be pretty much identical tests)
Why do need two methods that do the same thing?
Regardless, you should write separate tests.
generally unit tests should cover the public interface and not non-public members and your GenerateHexString probably shouldn't be public if it is only to be used by the other methods
your implementations are the same now, but they may diverge in the future. Without distinct test cases you may miss breaking changes introduced by someone changing one of those implementations
ultimately your tests shouldn't know or care about the internal implementation details of your code
One thing that might help in nUnit would be the TestCaseSource attribute. It would allow you to define the same test cases for both methods saving some duplication in your code.
It's a bad idea to create many interface methods to do the same thing. I also don't put overloads on interfaces. The problem this creates is that methods that have the same semantic meaning can have wildly divergent implementations. They might not in the simplest cases, but simple cases often become complex ones eventually.
I love extension methods for this problem.
public interface IAuthProvider
{
string GenerateKey();
}
public static class IAuthProviderExtensions
{
public static string GenerateSecret(this IAuthProvider provider)
{
return provider.GenerateKey();
}
}
Test:
[Test]
public void GenerateSecretIsAliasForGenerateKey()
{
var mockProvider = new Mock<IAuthProvider>();
var key = GenerateARandomStringSomehow();
mockProvider.Setup(p=>p.GenerateKey()).Returns(key);
Assert.That(mockProvider.Object.GenerateSecret(), Is.EqualTo(key));
}
I'm using the System.Composition namespace from the MEF for web and Windows Store apps NuGet package in a new ASP.NET MVC4 project.
I've read that in MEF2 you no longer use Lazy<IExtension, IExtensionMetadata>, but now you must provide a concrete type for the metadata view (and possibly use ExportFactory<> instead of Lazy<> ?).
However, I can't find any examples of how this should all work - just a few mentions of using a concrete type instead of an interface.
I've tried a few things, but keep getting the following error - "Export metadata for 'AccountID' is missing and no default value was supplied".
My code...
Creating the container (in Global.asax or App_Start folder):
// Get assemblies that will be providing imports and exports
var assemblies = GetAssemblies();
// Get conventions that will be used to find imports and exports
var conventions = GetConventions();
var container = new ContainerConfiguration().WithAssemblies(assemblies, conventions).CreateContainer();
// Create and apply a MefControllerFactory so controllers can be composed
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));
GetConventions() method:
private static ConventionBuilder GetConventions()
{
var conventionBuilder = new ConventionBuilder();
conventionBuilder.ForTypesDerivedFrom<IController>().Export();
conventionBuilder.ForTypesDerivedFrom<IExtension>().Export<IExtension>();
conventionBuilder.ForTypesMatching(t => t.Namespace != null && t.Namespace.EndsWith(".Parts")).Export().ExportInterfaces();
return conventionBuilder;
}
IExtension.cs:
public interface IExtension
{
void DoWork();
}
ExtensionMetadata.cs:
public class ExtensionMetadata
{
public int AccountID { get; set; }
}
ExtensionA.cs (same as ExtensionB.cs):
public void DoWork()
{
System.Diagnostics.Debug.WriteLine("ExtensionA doing work..");
}
ExtensionManager.cs:
public class ExtensionManager
{
private IEnumerable<ExportFactory<IExtension, ExtensionMetadata>> _extensions;
public ExtensionManager(IEnumerable<ExportFactory<IExtension, ExtensionMetadata>> extensions)
{
_extensions = extensions;
}
public void DoWork(int accountID)
{
foreach (var extension in _extensions)
{
if (extension.Metadata.AccountID == accountID)
{
extension.DoWork();
}
}
}
}
I think I'm missing something quite major here. Basically I want to lazily import all Extensions, check their metadata and if a condition is fulfilled have that extension do something.
Would really appreciate your feedback or any links to sample code / tutorials that cover my scenario.
Many thanks!
I think I've worked it out after reading this SO question.
I created a Metadata Attribute:
[MetadataAttribute]
public class ExtensionMetadataAttribute : ExportAttribute, IExtensionMetadata
{
public int AccountID { get; set; }
public ExtensionMetadataAttribute(int accountID) : base(typeof (IExtension))
{
AccountID = accountID;
}
}
Then modified ExtensionA.cs:
[ExtensionMetadata(1)]
public class ExtensionA : IExtension
{
public void DoWork()
{
System.Diagnostics.Debug.WriteLine("ExtensionA doing work..");
}
}
And now ExtensionManager.cs looks like this:
public class ExtensionManager : IExtensionManager
{
private readonly IEnumerable<ExportFactory<IExtension, ExtensionMetadata>> _extensions;
public ExtensionManager(IEnumerable<ExportFactory<IExtension, ExtensionMetadata>> extensions)
{
_extensions = extensions;
}
public void DoWork(int accountID)
{
foreach (var extension in _extensions)
{
if (extension.Metadata.AccountID == accountID)
{
using (var foo = extension.CreateExport())
{
foo.Value.DoWork();
}
}
}
}
}
This seems to do the trick, but I would still be interested in any feedback re best practices, performance issues etc.
Thanks!