Dependency Injection into Roslyn analyzers - c#

I have a custom Roslyn analyzer which inherits from DiagnosticAnalyzer. It is packed in VSIX extension which contains custom Package class. I'd like to pass an instance of a class with the settings (CodeAnalysisSettings instance) from the package to my DiagnosticAnalyzer.
I've tried to use MEF for that purpose. I've registered an instance of my settings class in VS Package using the following code:
protected override void Initialize()
{
base.Initialize();
IComponentModel componentModel = Package.GetGlobalService(typeof(SComponentModel)) as IComponentModel;
new CompositionContainer(componentModel.DefaultCatalog).ComposeExportedValue(
new CodeAnalysisSettings(...));
}
Analyzer looks as follows:
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MyAnalyzer : DiagnosticAnalyzer
{
[Import]
public CodeAnalysisSettings Settings { get; set; }
}
Settings class:
[Export]
public class CodeAnalysisSettings
{
public CodeAnalysisSettings()
{
}
public bool RecursiveAnalysisEnabled { get; }
}
For some reason Settings property is not imported - its value is always null.
Please help.

I've ended up using a service locator (CommonServiceLocator package) that consumes MEF container as a source.
Since VS infrastructure doesn't allow adding new registrations (IComponentModel.DefaultCatalog throws an exception which tells that this functionality is not supported anymore), I've created a new container inside Package.Initialize():
var container = new CompositionContainer(CompositionOptions.Default, componentModel.DefaultExportProvider);
container.ComposeExportedValue<CodeAnalysisSettings>(new CodeAnalysisSettings(...));
var serviceLocator = new MefServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => serviceLocator);
Roslyn diagnostic consumes this dependency:
var settings = CodeAnalysisSettings.Default;
if (ServiceLocator.IsLocationProviderSet)
settings = ServiceLocator.GetInstance<CodeAnalysisSettings>();

Related

How do I handle a configuration class that are loaded at runtime with dependency injection?

I'm currently trying to work with dependency injection and so far I love. But it's one thing I can't really get my head around and where my current solution just seems wrong.
I'm working with WPF, MVVM and many of the classes I inject need an instance of a project configuration class that isn't initialized until the user create or open a new project in the application.
So my current solution is to have a "ConfigurationHandler" with load/save method and a property that hold an instance of the configuration class after it's loaded. I inject ConfigurationHandler to the others classes and then they can access the configuration after it's loaded. But it seems weird to let classes that never should save/load configuration handle the whole "ConfigurationHandler" and 100% they would just use it to access the configuration instance likt this:
var configuration = configurationHandler.Configuration;
Another problem is that if they try to access the configuration before it's loaded they will get exception (should not really happen as you can't do anything before a project is created/loaded, but still).
But the only other solution I can think of is to use "intialize" methods after a project is created/open but that seems just as bad.
So how do you usually handle cases like this?
Edit: Should add that this configuration class handle information like project path, project name, etc so have nothing to do with the dependency injection itself.
If your configuration is static (read: It's only read during startup of your application, such as from project.json or Web.Config), you can also set it during app startup/the composition root.
The new ASP.NET 5 uses it heavily and it works very well. Basically you will have an IConfiguration<T> interface and a POCO class, which you set up during the app startup and can resolve/inject it into your services.
public interface IConfiguration<T> where T : class
{
T Configuration { get; }
}
And it's default implementation
public interface DefaultConfiguration<T> where T : class
{
private readonly T configuration;
public T Configuration {
return configuration;
}
public DefaultConfiguration<T>(T config)
{
this.configuration = this.configuration;
}
}
And your POCO class
public class AppConfiguration
{
public string OneOption { get; set; }
public string OtherOption { get; set; }
}
In your composition root, you would then register it, like
// read Web.Config
Configuration rootWebConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(null);
container.AddSingleton<IConfiguration<AppConfiguration>>(new DefaultConfiguration<AppConfiguration>(
new AppConfiguration
{
OneOption = rootWebConfig.AppSettings.Settings["oneSetting"],
OtherOption = rootWebConfig.AppSettings.Settings["otherSetting"],
})
);
And finally, all you have to declare in your services is
public class MyService : IMyService
{
public MyService(IUserRepository, IConfiguration<AppConfiguration> appConfig)
{
...
if(appConfig.OneOption=="someValue") {
// do something
};
}
}
Finally you can make this a bit easier to configure, if you write an extension method like
public static class MyContainerExtension
{
public static void Configure<T>(this IMyContainer container, Action<T> config) where T : class, new()
{
var t = new T();
config(t);
container.AddSingelton<IConfiguration<T>>(t);
}
}
Then all you need to do is
container.Configure<AppConfiguration>(
config =>
{
config.OneOption = rootWebConfig.AppSettings.Settings["oneSetting"],
config.OtherOption = rootWebConfig.AppSettings.Settings["otherSetting"],
})
);
to set it up
Instead of Constructor Injection, consider using an Ambient Context approach.
The last type of DI we’ll discuss is making dependencies available
through a static accessor. It is also called injection through the
ambient context. It is used when implementing cross-cutting concerns.
This is a good option if the classes that need access to your configuration are of different types in different layers or libraries - i.e. is a true cross-cutting concern.
(Quote source)
Example, based on the classic Time Provider one from [Dependency Injection in .NET][2]
abstract class CustomConfiguration
{
//current dependency stored in static field
private static CustomConfiguration current;
//static property which gives access to dependency
public static CustomConfiguration Current
{
get
{
if (current == null)
{
//Ambient Context can't return null, so we assign a Local Default
current = new DefaultCustomConfiguration();
}
return current;
}
set
{
//allows to set different implementation of abstraction than Local Default
current = (value == null) ? new DefaultCustomConfiguration() : value;
}
}
//service which should be override by subclass
public virtual string SomeSetting { get; }
}
//Local Default
class DefaultCustomConfiguration : CustomConfiguration
{
public override string SomeSetting
{
get { return "setting"; }
}
}
Usage
CustomConfiguration.Current.SomeSetting;
There are other DI Patterns that could be used, but require changes to the class that need it. If Configuration is a cross cutting concern Ambient Context could be the best fit.
Constructor Injection Example
public SomeClass(IConfiguration config)
{
}
Property Injection
public SomeClass()
{
IConfiguration configuration { get; set; }
}
Method Injection
public SomeClass()
{
public void DoSomethingNeedingConfiguation(IConfiguration config)
{
}
}
There is also Service Locator, but Service Locator is (IMO) an anti-pattern.

Load Prism module with stand-alone module manager

The WPF application I am working on will have numerous add-ins to extend its functionality. Each add-in will consist of one or several assemblies (usually a "main" add-in assembly with separate assemblies for presentation-layer components and views) but will be treated by the application as a single Prism module. Prism will be used to discover and load the add-ins, with MEF used to ensure that the Prism module can access dependencies in the add-in's related assemblies. The Prism module's Initialize method will be responsible for, amongst other things, configuring the IoC container (in this case, Unity). In a deployed scenario, this will all be loaded and managed with a MefBootstrapper at startup.
My problem occurs when trying to unit test an add-in. To keep the add-in code semi-isolated from the main application, each add-in will also have its own unit test assemblies. One of these test assemblies will be responsible for checking registration of services with the IoC container. In the test scenario, I don't want to use a bootstrapper to load the Prism modules as they have dependencies that I don't want to introduce to my test assemblies. I have therefore written my test fixture base class such that it creates its own MefModuleManager to load the module to be tested.
ResolutionTestBase.cs
public abstract class ResolutionTestBase
{
[ClassInitialize]
public static void TestFixtureInitialise(TestContext context)
{
// Create the main resolution container.
var container = new UnityContainer();
// Install the service locator.
var locator = new UnityServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => locator);
}
// Here go some helper methods for performing resolution tests.
protected IUnityContainer Container
{
get { return ServiceLocator.Current.GetService(typeof(IUnityContainer)) as IUnityContainer; }
}
}
AddInResolutionTestBase.cs
public abstract class AddInResolutionTestBase:ResolutionTestBase
{
static AddInResolutionTestBase()
{
Logger = new EmptyLogger();
}
[TestInitialize]
public virtual void TestInitialise()
{
// Create MEF catalog.
var aggregateCatalog = new AggregateCatalog();
foreach (var testAssembly in TestAssemblies)
{
aggregateCatalog.Catalogs.Add(new AssemblyCatalog(testAssembly));
}
// Load module manager.
var container = new CompositionContainer(aggregateCatalog);
var serviceLocator = new MefServiceLocatorAdapter(container);
var parts = new DownloadedPartCatalogCollection();
var moduleInitialiser = new MefModuleInitializer(serviceLocator, Logger, parts, aggregateCatalog);
var moduleManager = new MefModuleManager(moduleInitialiser, ModuleCatalog, Logger);
moduleManager.ModuleTypeLoaders = new[] { new MefFileModuleTypeLoader() };
moduleManager.Run();
}
protected static ILoggerFacade Logger { get; private set; }
protected abstract IModuleCatalog ModuleCatalog { get; }
protected abstract IEnumerable<Assembly> TestAssemblies { get; }
}
For the add-ins, they have a stand-alone class in their main assembly to implement the requirements of a Prism module, and Unity extensions to configure the container.
Module.cs
[ModuleExport("AddInModule", typeof(Module), InitializationMode = InitializationMode.OnDemand)]
public class Module : IModule
{
private readonly IEnumerable<IUnityContainerExtensionConfigurator> _extensions;
[ImportingConstructor]
public Module([ImportMany]IEnumerable<IUnityContainerExtensionConfigurator> extensions)
{
_extensions = extensions;
}
public void Initialize()
{
// Load the dependency injection container.
var container = ServiceLocator.Current.GetService(typeof(IUnityContainer)) as IUnityContainer;
if (container != null)
{
foreach (var extension in _extensions)
{
container.AddExtension((UnityContainerExtension) extension);
}
}
}
}
ContainerInstallerExtension.cs (in the add-in's main assembly)
[Export(typeof(IUnityContainerExtensionConfigurator))]
public class ContainerInstallerExtension : UnityContainerExtension
{
protected override void Initialize()
{
// perform container configuration here.
}
}
PresentationInstallerExtension.cs (in the add-in's presentation assembly)
[Export(typeof(IUnityContainerExtensionConfigurator))]
public class PresentationInstallerExtension:UnityContainerExtension
{
protected override void Initialize()
{
// perform container configuration here.
}
}
AddInResolutionTest.cs (in the add-in's IoC test assembly)
[TestClass]
public class AddInResolutionTest : AddInResolutionTestBase
{
private IEnumerable<Assembly> _testAssemblies;
private IModuleCatalog DoGetModuleCatalog()
{
var moduleInfo = new ModuleInfo("AddInModule", typeof (Module).AssemblyQualifiedName)
{
InitializationMode = InitializationMode.WhenAvailable,
Ref = typeof (Module).Assembly.CodeBase
};
return new ModuleCatalog(new[] {moduleInfo});
}
protected override IModuleCatalog ModuleCatalog
{
get { return DoGetModuleCatalog(); }
}
protected override IEnumerable<Assembly> TestAssemblies
{
get { return _testAssemblies ?? (_testAssemblies = new[] { typeof(ContainerInstallerExtension).Assembly, typeof(PresentationInstallerExtension).Assembly }); }
}
[TestMethod]
public void ResolveSomeService()
{
// perform resolution test here.
}
}
Of note with the resolution test fixture, the "test assemblies" are linked to the IoC test assembly with project references and referred to directly by type (rather than using a directory-scan catalog) so I could avoid having to use a post-build event to copy assemblies to a common folder for testing.
When I run the unit tests (as-is), I get an exception indicating the module manager failed to load the Prism module:
Initialization method AddInResolutionTest.TestInitialise threw exception. Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException: Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException: Failed to load type for module AddInModule.
If this error occurred when using MEF in a Silverlight application, please ensure that the CopyLocal property of the reference to the MefExtensions assembly is set to true in the main application/shell and false in all other assemblies.
Error was: Object reference not set to an instance of an object.. ---> System.NullReferenceException: Object reference not set to an instance of an object..
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.LoadModuleType(ModuleInfo moduleInfo)
--- End of inner exception stack trace ---
at Microsoft.Practices.Prism.Modularity.ModuleManager.HandleModuleTypeLoadingError(ModuleInfo moduleInfo, Exception exception)
at Microsoft.Practices.Prism.Modularity.ModuleManager.IModuleTypeLoader_LoadModuleCompleted(Object sender, LoadModuleCompletedEventArgs e)
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.RaiseLoadModuleCompleted(LoadModuleCompletedEventArgs e)
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.RaiseLoadModuleCompleted(ModuleInfo moduleInfo, Exception error)
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.LoadModuleType(ModuleInfo moduleInfo)
at Microsoft.Practices.Prism.Modularity.ModuleManager.BeginRetrievingModule(ModuleInfo moduleInfo)
at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModuleTypes(IEnumerable`1 moduleInfos)
at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModulesWhenAvailable()
at Microsoft.Practices.Prism.Modularity.ModuleManager.Run()
at AddInResolutionTestBase.TestInitialise() in AddInResolutionTestBase.cs: line xx
At the point of calling moduleManager.Run() nothing in my code is null so it is not clear to me what the "real" problem is.
I have tried various changes to resolve the problem including:
calling moduleManager.LoadModule() instead of moduleManager.Run() in AddInResolutionTestBase.cs
manipulating the State of the ModuleInfo created in AddInResolutionTest to bypass the problem in the module manager
Any other changes I've made have resulted in different errors, but still indicate a problem with the module manager trying to load the Prism module.
Is there some additional step required to correctly configure the module manager to be able to load modules in this way, bearing in mind that some of the usual overhead (such as the logger) is not required for the unit tests?
With the aid of a de-compiler, I was able to figure out the "missing pieces" and make some changes to ensure that all the required components are registered/installed for the module manager to be able to initialise the module(s) for testing. For anyone who is interested:
public abstract class AddInResolutionTestBase:ResolutionTestBase
{
private CompositionContainer _container;
private IModuleCatalog _moduleCatalog;
private IEnumerable<object> _testEntities;
private IEnumerable<ModuleInfo> _testModuleInformation;
static AddInResolutionTestBase()
{
Logger = new EmptyLogger();
}
[TestInitialize]
public virtual void TestInitialise()
{
// Create MEF catalog.
AggregateCatalog = CreateAggregateCatalog();
ConfigureAggregateCatalog();
AggregateCatalog = DefaultPrismServiceRegistrar.RegisterRequiredPrismServicesIfMissing(AggregateCatalog);
ConfigureContainer();
// Initialise modules to be tested.
CompositionContainer.GetExportedValue<IModuleManager>().Run();
}
#region Protected Methods
protected virtual void ConfigureAggregateCatalog()
{
var testAssemblies = TestEntities.OfType<Assembly>();
foreach (var testAssembly in testAssemblies)
{
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(testAssembly));
}
if (TestEntities.Any(entity => entity is System.Type))
{
var catalog = new TypeCatalog(TestEntities.OfType<System.Type>());
AggregateCatalog.Catalogs.Add(catalog);
}
}
protected virtual void ConfigureContainer()
{
CompositionContainer.ComposeExportedValue<ILoggerFacade>(Logger);
CompositionContainer.ComposeExportedValue<IModuleCatalog>(ModuleCatalog);
CompositionContainer.ComposeExportedValue<IServiceLocator>(new MefServiceLocatorAdapter(CompositionContainer));
CompositionContainer.ComposeExportedValue<AggregateCatalog>(AggregateCatalog);
}
protected virtual AggregateCatalog CreateAggregateCatalog()
{
return new AggregateCatalog();
}
protected virtual CompositionContainer CreateContainer()
{
return new CompositionContainer(AggregateCatalog);
}
protected virtual IModuleCatalog CreateModuleCatalog()
{
return new ModuleCatalog(TestModuleInformation);
}
protected abstract IEnumerable<object> GetTestEntities();
protected abstract IEnumerable<ModuleInfo> GetTestModuleInformation();
#endregion
#region Protected Properties
protected AggregateCatalog AggregateCatalog { get; set; }
protected CompositionContainer CompositionContainer
{
get { return _container ?? (_container = CreateContainer()); }
}
protected static ILoggerFacade Logger { get; private set; }
protected IModuleCatalog ModuleCatalog
{
get { return _moduleCatalog ?? (_moduleCatalog = CreateModuleCatalog()); }
}
protected IEnumerable<object> TestEntities
{
get { return _testEntities ?? (_testEntities = GetTestEntities()); }
}
protected IEnumerable<ModuleInfo> TestModuleInformation
{
get { return _testModuleInformation ?? (_testModuleInformation = GetTestModuleInformation()); }
}
#endregion
}
This test base class now mimics to some extent what normally goes on in the boostrapper when the application normally starts. The (resolution) tests in each of the add-ins now only need to provide a list of the (exported) container extensions and module information for the Prism module that represents the add-in (in addition to the actual resolution tests!)

MEF isn't loading my plugin

I've implemented a very small plugin system in C# with MEF. But my plugins won't be loaded. In the Aggregate-Catalog I can see my plugin listed. But, after I'll compose these parts, there isn't my plugin in the plugin list, what I'm doing wrong?
Here's a snippet of my code:
Plugin-Loader:
[ImportMany(typeof(IFetchService))]
private IFetchService[] _pluginList;
private AggregateCatalog _pluginCatalog;
private const string pluginPathKey = "PluginPath";
...
public PluginManager(ApplicationContext context)
{
var dirCatalog = new DirectoryCatalog(ConfigurationManager.AppSettings[pluginPathKey]);
//Here's my plugin listed...
_pluginCatalog = new AggregateCatalog(dirCatalog);
var compositionContainer = new CompositionContainer(_pluginCatalog);
compositionContainer.ComposeParts(this);
}
...
And here, the plugin itself:
[Export(typeof(IFetchService))]
public class MySamplePlugin : IFetchService
{
public MySamplePlugin()
{
Console.WriteLine("Plugin entered");
}
...
}
You're doing this wrong. The ImportMany attribute of the _pluginList field doesn't make any sense, since the the plugin manager instance will be created by you, not by the DI container.
You have to create another class that will import all your plugins.
[Export]
class SomeClass
{
readonly IFetchService[] pluginList;
[ImportingConstructor]
public SomeClass([ImportMany(typeof(IFetchService))]IFetchService[] pluginList)
{
this.pluginList = pluginList;
}
}
Now, you can let the DI container compose an instance of this SomeClass for you. You will see that its pluginList field contains your plugin reference.

Visual Studio Package Initialize method does not call when debugging

Currently, I'm developing an extension to Visual Studio 2010 using MEF and I need to initialize my global state. I'm trying to do it in Package.Initialize method
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0.0.0", IconResourceID = 400)]
[Guid("1AF4B41B-F2DF-4F49-965A-816A103ADFEF")]
public sealed class MyPackage : Package
{
protected override void Initialize()
{
ContainerConfigurator.Configure();
ContainerConfigurator.IsInitialized = true;
base.Initialize();
}
}
Also I have a MEF classifier provider that uses this state
[Export(typeof(IClassifierProvider))]
[Name("This is my provider")]
[ContentType("DebugOutput")]
[ContentType("Output")]
public class MyClassifierProvider : IClassifierProvider
{
[Import]
private IClassificationTypeRegistryService _classificationRegistry = null; // MEF
public IClassifier GetClassifier(ITextBuffer textBuffer)
{
// This always false
if (!ContainerConfigurator.IsInitialized)
throw new InvalidOperationException();
return textBuffer.Properties.GetOrCreateSingletonProperty(() => new TypedClassifier(ServiceLocator.Current, _classificationRegistry));
}
}
Both of package and MEF classifier are in the same assembly. When I start debugging and place a breakpoint I see that this assemly is loaded. But MyClassifierProvider has been initialized before MyPackage.Initialize call. So I can't initialize my global state before any of MEF components is started. Can anyone explain why and how can I avoid that behavior?
Thanks
I've found the answer. It is necessary to add ProvideAutoLoad attribute
http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.vsconstants(v=vs.80).aspx
http://dotneteers.net/blogs/divedeeper/archive/2008/03/23/LVNSideBar1.aspx
so the final class declaration is
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0.0.0", IconResourceID = 400)]
[Guid("1AF4B41B-F2DF-4F49-965A-816A103ADFEF")]
[ProvideAutoLoad("ADFC4E64-0397-11D1-9F4E-00A0C911004F")]
public sealed class MyPackage : Package

MEF Constructor Injection

I'm trying to figure out MEF's Constructor Injection attribute. I have no idea how I tell it to load the constructor's parameters.
This is the property I'm trying to load
[ImportMany(typeof(BUsers))]
public IEnumerable<BUsers> LoadBUsers { get; set; }
Here is the code I'm using to import the assemblies.
try
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new DirectoryCatalog("DI"));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
Here is the class I'm trying to load
[Serializable]
[Export(typeof(BUsers))]
public class EditProfile : BUsers
{
[ImportingConstructor]
public EditProfile(string Method, string Version)
{
Version = "2";
Action = "Edit";
TypeName = "EditProfile";
}
When you use the ImportingConstructor attribute, the parameters to the constructor become imports. By default, what you are importing (the contract name) is based on the type of the parameter or property that your are importing into. So in this case the contract type for both your imports is string, and there's no real difference between the first and second parameter.
It looks like you are trying to use imports to supply configuration values, which isn't necessarily what it was designed for. To get it to do what you want, you should override the contract name for each of the parameters, like this:
[ImportingConstructor]
public EditProfile([Import("Method")] string Method, [Import("Version")] string Version)
{ }
Then you need exports for Method and Version in your container. One way to do this is just to add them directly:
var container = new CompositionContainer(catalog);
container.ComposeExportedValue("Method", "MethodValue");
container.ComposeExportedValue("Version", "2.0");
container.ComposeParts(this);
(Note that ComposeExportedValue is actually an extension method defined on the static AttributedModelServices class.)
If you want to read these values from a configuration file of some sort, you could create your own export provider which reads the configuration and provides the values in it as exports to the container.
An alternative way to handle this would be to just import an interface that provides access to the configuration values by name, and get the values you need from the body of the constructor.
I like Daniel's solution; however, only one thing I see is the tight coupling of parameter names between the actor (who creates CompopositionContrainer()) and Export part with [ImportingConstructor] for customized CTOR. For example, "Method" has two be matched in both places. It makes hard to maintain the Export part if the actor and Export part are in difference projects.
If it is possible, I would add the second CTOR to the Export part class. For example:
[Export(typeof(BUsers))]
public class EditProfile : BUsers
{
[ImportingConstructor]
public EditProfile(EditProfileParameters ctorPars)
: this(ctorPars.Method, ctorPars.Version) {}
public EditProfile(string Method, string Version)
{
Version = "2";
Action = "Edit";
TypeName = "EditProfile";
}
The class of EditProfileParameters should be straightforward: two properties of Method and Version:
[Export]
public class EditProfileParameters{
public string Method { get; set; }
public string Version { get; set; }
}
The key point is to add Export attribute to the class. Then MEF should be able to map this class to the parameter of EditProfile's CTOR.
Here is example to add the Export part to container:
var container = new CompositionContainer(catalog);
var instance1 = new EditProfileParameters();
// set property values from config or other resources
container.ComposeExportedValue(instance1);
container.ComposeParts(this);
Although late to the game, here's another approach that leverages a lesser-known feature of MEF: Property Exports
public class ObjectMother
{
[Export]
public static EditProfile DefaultEditProfile
{
get
{
var method = ConfigurationManager.AppSettings["method"];
var version = ConfigurationManager.AppSettings["version"];
return new EditProfile(method,version);
}
}
}
No usages are required for ObjectMother for this to work, and no attributes are required on EditProfile.

Categories

Resources