No configuration required for ILogger? - c#

I'm trying out Ninject's Logging extension (v3.0) with NLog (v2.0) and, from what I've read, I'm not supposed to configure anything, the thing is like auto-magical and all I need to do is declare a ILogger dependency wherever I need one.
I kept the nlog.config file I already had, and removed all code that created loggers and instead put ILogger in my classes' constructors.
I'm getting an ActivationException at the composition root, ...Ninject can't resolve ILogger.
What am I missing?
What I have essentially boils down to this:
public static void Main(string[] args)
{
// nothing special here, binds IMyApp to MyApp.
using (var kernel = new StandardKernel(new NinjectConfig()))
{
var app = kernel.Get<IMyApp>(); // MyApp has a ILogger dependency **blows up here**
app.Run(args);
}
}
This is a console app, for what it's worth... Is the logging extension supposed to work like this?
The documentation says:
make sure you have the kernel load a new Log4NetModule or NLogModule before requesting the type instance
But if I do this:
using (var kernel = new StandardKernel(new NLogModule(), new NinjectConfig()))
{
...
...ReSharper doesn't know what NLogModule is.

Downloading the nuget package for Ninject.Extensions.Logging isn't enough.
You also need to download the nuget package for Ninject.Extensions.Logging.nlog2, which contains the NLogModule class that's needed to bind the ILogger interface.
There's also an available nuget package for log4net logging available.
This:
var kernel = new StandardKernel(new NinjectConfig())
Is all that's required to make everything work - no need to explicitly load a new NLogModule().

Related

Using dependency injection in SpecFlow step-file

We're using Unity as our dependency injection framework.
I want to create an acceptance test and need an instance of DossierService.
Unfortunately I get the following exception:
BoDi.ObjectContainerException: 'Interface cannot be resolved [...]'
[Binding]
public class DossierServiceSteps : BaseSteps
{
private IDossierService dossierService;
public DossierServiceSteps(IDossierService dossierService)
{
this.dossierService = dossierService;
}
}
What exactly is BoDi? I can't find any useful information..
How can I tell SpecFlow to use the normal Unity container?
Thanks in advance
Edit:
I've tried using SpecFlow.Unity like so:
public static class TestDependencies
{
[ScenarioDependencies]
public static IUnityContainer CreateContainer()
{
var container = UnityConfig.GetConfiguredContainer();
container.RegisterTypes(typeof(TestDependencies).Assembly.GetTypes().Where(t => Attribute.IsDefined(t, typeof(BindingAttribute))),
WithMappings.FromMatchingInterface,
WithName.Default,
WithLifetime.ContainerControlled);
return container;
}
}
In UnityConfig the types are correctly registered
container.RegisterType<IDossierService, DossierService>(new InjectionConstructor(typeof(IDataService), typeof(IDossierRepository), typeof(IDbContext), true));
But I still get the same exception. When I put a breakpoint at the start of the CreateContainer() method of TestDependencies it doesn't break...
For anyone looking for available plugins/libraries that support DI in Specflow project: https://docs.specflow.org/projects/specflow/en/latest/Extend/Available-Plugins.html#plugins-for-di-container
I prefer - https://github.com/solidtoken/SpecFlow.DependencyInjection
Example
Create DI container:
[ScenarioDependencies]
public static IServiceCollection CreateServices()
{
var services = new ServiceCollection();
Config config = JObject.Parse(File.ReadAllText("config.json")).ToObject<Config>();
services.AddSingleton(config);
services.AddScoped<DbConnections>();
services.AddScoped<ApiClients>();
return services;
}
Consume dependencies (via parameterized constructors):
[Binding]
public sealed class CalculatorStepDefinitions
{
private readonly DbConnections dbConnections;
public CalculatorStepDefinitions(DbConnections dbConnections) => this.dbConnections = dbConnections;
...
}
We solved this problem by implementing SpecFlow RuntimePlugin. In our case it was Castle.Windsor, but principle is the same. First you define the plugin which override default SpecFlow Instance Resolver:
public class CastleWindsorPlugin : IRuntimePlugin
{
public void Initialize(RuntimePluginEvents runtimePluginEvents, RuntimePluginParameters runtimePluginParameters)
{
runtimePluginEvents.CustomizeScenarioDependencies += (sender, args) =>
{
args.ObjectContainer.RegisterTypeAs<CastleWindsorBindingInstanceResolver, IBindingInstanceResolver>();
};
}
}
Where in CastleWindsorBindingInstanceResolver we needed to implement single method: object ResolveBindingInstance(Type bindingType, IObjectContainer scenarioContainer);. This class contains container and resolution (in your case instance of IUnityContainer. I recommend to inject to the container instance of self, so that you could inject the instance of IUnityContainer to SpecFlow binding classes)
This plugin needs to be in separate assembly and you load that to your test project like adjusting app.config like this:
<specFlow>
<plugins>
<add name="PluginAssemblyName" path="." type="Runtime" />
</plugins>
...
</specFlow>
What exactly is BoDi? I can't find any useful information..
BoDI is a very basic Dependency Injection framework that ships within Specflow. You can find its code repository here.
See this entry from the blog of SpecFlow's creator, Gáspár Nagy (emphasis mine):
SpecFlow uses a special dependency injection framework called BoDi to handle these tasks. BoDi is an embeddable open-source mini DI framework available on GitHub. Although it is a generic purpose DI, its design and features are driven by the needs of SpecFlow. By the time the SpecFlow project started, NuGet had not existed yet, so the libraries were distributed via zip downloads and the assemblies had to be referenced manually. Therefore we wanted to keep the SpecFlow runtime as a single-assembly library. For this, we needed a DI framework that was small and could be embedded as source code. Also we did not want to use a well-known DI framework, because it might have caused a conflict with the version of the framework used by your own project. This led me to create BoDi.
You can find an example of how to register types and interfaces in BoDI here:
[Binding]
public class DependencyConfiguration
{
private IObjectContainer objectContainer;
public DependencyConfiguration(IObjectContainer objectContainer)
{
this.objectContainer = objectContainer;
}
[BeforeScenario(Order = 0)]
public void ConfigureDependencies()
{
if (...)
objectContainer.RegisterTypeAs<RealDbDriver, IControllerDriver>();
else
objectContainer.RegisterTypeAs<StubDbDriver, IControllerDriver>();
}
}
However, be warned that (in the words of Gáspár Nagy):
Although it is possible to customize the dependency injection used by SpecFlow, it already scratches the boundaries of BoDi’s capacity. A better choice would be to use a more complex DI framework for this.
In this situation usually you should use Mock of your Interface.

How to use RegisterType(Type).As(Type)

My MVC project uses Autofac 3.0 and I want to RegisterType by reflection.
First defined one interface and implementation named like IRegisterDemo and RegisterDemo less parameters.
I tried to use builder.RegisterType(typeof(RegisterDemo)).As(typeof(IRegisterDemo)) in my Application_Start method, it successed, but it's not my purpose.
I want to define one attribute like UseAutofacAttribute to reflect the types and use RegisterType(ImplType).As(InterfaceType), when I wrote the code in Application_Start, it worked, so I built another project to do this further and referenced the project dll in my MVC project, and in Application_Start just run one static method, unfortunately it failed.
So I want to know the reason and how to change?
If you want to make your registrations attribute based, I'd suggest you use Autofac's MEF adapter. It will let you decorate your types with the ExportAttribute and be picked up by your ContainerBuilder.
Example
[Export(typeof(IRegisterDemo))] // <-- This exports RegisterDemo as IRegisterDemo
public class RegisterDemo : IRegisterDemo { }
// Example method to register assemblies with ContainerBuilder
public void RegisterPartsFromReferencedAssemblies(ContainerBuilder builder)
{
// Get referenced assemblies
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();
// Create an AssemblyCatalog from each assembly
var assemblyCatalogs = assemblies.Select(x => new AssemblyCatalog(x));
// Combine all AssemblyCatalogs into an AggregateCatalog
var catalog = new AggregateCatalog(assemblyCatalogs);
// Register the catalog with the ContainerBuilder
builder.RegisterComposablePartCatalog(catalog);
}
This example shows how to export a type and how to register referenced assemblies with the ContainerBuilder. You can then do container.Resolve<IRegisterDemo>() to get the exported part (or have it injected into a class).
NOTE: This will register exported parts from all referenced assemblies, including:
Assemblies specified in the assemblies element of the Web.config file
Assemblies built from custom code in the App_Code directory
Assemblies in other top-level folders

Removing from MEF container

This question seems to have been asked several times, but I have yet to find any answer that actually works. Very simply, how do I remove something from a MEF container.
Even the code shown here https://mef.codeplex.com/wikipage?title=Parts%20Lifetime under AddPart/RemovePart doesn't work as it won't compile as it is listed. The code shows this:
var catalog = new AssemblyCatalog(typeof(Program).Assembly);
var container = new CompositionContainer(catalog);
var root = new Root();
// add external part
container.ComposeParts(root);
// ... use the composed root instance
// removes external part
batch = new CompositionBatch();
batch.RemovePart(root);
container.Compose(batch);
This won't compile because the call to RemovePart requires a ComposablePart which root is clearly not. Other simple examples show how to create the part and remove the part, but a reference to the created part (as ComposablePart) is remembered so the removal just uses that reference. I don't want to keep a reference to each part in the container whenever they are created, I just want to remove a part from the container at any arbitrary point in my application without having to keep a reference to it throughout.
Here is what I am trying to do using the exact same pattern listed in the documentation linked above:
public class Program
{
[Import]
private IClass myClass;
public Program()
{
var container = new CompositionContainer(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
container.ComposeParts(this);
var partToRemove = container.GetExport<IClass>();
var batch = new CompositionBatch();
batch.RemovePart(partToRemove);
container.Compose(batch);
}
public static void Main(string[] args)
{
var program = new Program();
}
}
But this gives me the following compilation error:
Argument 1: cannot convert from
'System.Lazy' to
'System.ComponentModel.Composition.Primitives.ComposablePart' C:\Users\irbldr.CORP\Documents\Visual
Studio 2012\Projects\ConsoleApplication4\Program.cs
Which is exactly the same error I get if I use the code directly from the documentation linked above.
Is there no way to simply remove something from the MEF container?
This link could be helpful: How do I get MEF to recompose when I change a part?
But long story short: you can remove from MEF an instance, but you cannot remove an export definition without to drop the complete catalog, recompose and filter it (simply not add something).

How to Avoid Infinite Loops, Best Practices

I have a class in c# to help me log errors (ErrorClass).
The class has 3 methods. Log Error to: File System, Windows Event,
Email.
2 of the 3 methods require settings like "to email", or "directory path".
Settings are stored in the registry
I use dependency injection to instantiate the RegistryClass inside the ErrorClass
.
This is how I instantiate the ErrorHandle Class inside the Registry Class
ErrorHandle _ErrorHandle = new ErrorHandle();
And here is how I instantiate the Registry Class inside the ErrorHandle Class
RegistryTools _GetRegistry = new RegistryTools();
I have a class to help me retrieve values from the registry (RegistryClass)
The registry class needs to handle errors
I use dependency injection to instantiate the errorClass inside the RegistryClass
When I use dependency injection in both classes, an Endless LOOP is created when there is an error.
What is the suggested way or best practice of handling this situation:
Should I access the registry inside the ErrorClass?
Should I not ErrorHandle the RegistryClass?
Should I create a separate ErroHandle procedure for the
RegistryClass?
Don't re-invent this wheel. There is a tried and tested open source logging framework for .NET available, log4net. If you feel the need to use DI with it, you can do that too. log4net uses an XML file for configuration, which is much more accessible and less fraught with peril than dealing with the registry. It also swallows its own errors and makes them accessible via a debugging trace.
What mechanism are you using for DI? If you use setter injection there should be nothing to stop you doing something like:
var logger = new ErrorClass();
var registry = new RegistryClass();
logger.Registry = registry;
registry.Logger = logger;

Class design/best approach for initializing a user configuration file

I want to initialize a user configuration through a user configuration file. The path for the file can be read from the registry.
The file is located in a folder with the user's name.
So I need the following functionality:
Reading a string from registry
building the path to the configuration file
Reading the file into a configuration object
Now there are several approaches to handle this:
First, I need
one "helper"-class for getting the file path (let's call it Shared)
one "container"-class for the configuration information (let's call it Configuration)
So, Shared has a function/property like UserConfigurationFile which returns the path to the configuration file.
To get the path to the file I have a function InitializeUserConfigurationFile() which is called in the constructor of Shared:
class Shared {
public Shared()
{
InitializeUserConfigurationFile();
}
void InitializeUserConfigurationFile()
{
//
// Reads username
//
//
// Reads path from Registry
//
//
// etc.
//
}
//
// etc.
//
}
Any better suggestions?
When I want to Initialize my Container I have different options:
Is it best to initialize the user configuration within the constructor?
Sth. like:
class Container
{
Shared shared = new Shared();
public Container()
{
InitializeUserConfiguration();
}
void InitializeUserConfiguration()
{
LoadConfiguration(shared.UserConfigurationFile);
}
void LoadConfiguration(string filename)
{
//
// Initializes all parameters frome filename
//
}
}
Or through two steps (through an own method LoadConfiguration())?
Sth. like:
Shared shared = new Shared();
Container container = new Container();
container.LoadConfiguration(shared.UserConfigurationFile);
Or inside the constructor of Container by delivering a filename?
Sth. like:
Shared shared = new Shared();
Container container = new Container(shared.UserConfigurationFile);
or everything in Container..?
There are so many ways...
I hope somebody knows a best-approch...
Regards,
Inno
It is better to use standard configuration classes exist in .net. Such as ApplicationSettingsBase and Configuration.
Here you can find good article series:
Unraveling the Mysteries of .NET 2.0 Configuration
Unraveling the Mysteries of .NET 2.0 Configuration
Cracking the Mysteries of .NET 2.0 Configuration
For best practices, don't use the registry, and don't reinvent the wheel.
Since you didn't mention it, have you looked at the System.Configuration namespace?
The .NET Framework constains a perfectly good configuration system that is well tested. It is also the domain of Sys Admins, who also know about config files and the accompanying tools.
So it is unclear why you are reinventing the wheel, possibly making it a little less round.
There are practical reasons to shun the Registry (distribution, backup) but also, as arbiter points out, it is not going to move to other (future) platforms. Did you notice that those namespaces are not starting with System ?

Categories

Resources