Ninject to load different implementations - c#

I have a web service (let's call it WebSite) that uses an Interface (IDataService). the webservice project implements a "fake" service with hardcoded objects (DesignDataService) that I use to develop the websit while I wait for my colleagues to build the real implementation (BreadDataService).
My NinjectWebCommon is currently like this:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IDataService>().To<DesignDataService>();
}
What I want is to be able to provide my colleague a way to test the BreadDataService on my WebService, while I can go on using DesignDataService. I can't use the BreadDataService on my machine because it requires some assemblies that I don't have (+ the database).
So, what is the approach here? The current dependency tree is like that:
ServiceCore (defines IDataService)
WebSite uses ServiceCore
BreadDataService uses ServiceCore
I don't want to reference the BreadDataService project inside the WebSite Project, I was maybe thinking about a folder in WebSite where they can put the BreadDataService dll and ninject takes it depending on some configuration in web.config.
Hints?

Something like this will do the trick
load external assemblies
search for an implementation
default to your design time version if none are found
Here's the basic code
IEnumerable<Assembly> assemblies = this.LoadAssemblies(#"C:\Temp");
Type implementation = FindImplementation(assemblies, typeof(IDataService));
IKernel kernel = new StandardKernel();
kernel.Bind<IDataService>().To(implementation ?? typeof(DesignDataService));
This method will load external assemblies (such as plugins) from a specific folder
private IEnumerable<Assembly> LoadAssemblies(string folder)
{
IEnumerable<string> dlls =
from file in new DirectoryInfo(folder).GetFiles()
where file.Extension == ".dll"
select file.FullName;
IList<Assembly> assemblies = new List<Assembly>();
foreach (string dll in dlls)
{
try
{
assemblies.Add(Assembly.LoadFile(dll));
}
catch
{
}
}
return assemblies;
}
And this method will search a set of assemblies for an implementation. Please note that I have specifically used SingleOrDefault() so that this will fail if there is more than one implementation.
private Type FindImplementation(
IEnumerable<Assembly> assemblies,
Type serviceType)
{
var implementationType = (
from dll in assemblies
from type in dll.GetExportedTypes()
where serviceType.IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
select type)
.SingleOrDefault();
return implementationType;
}

I used qujck approach to build these extension methods.
The main difference is that it relies on Ninject.Extensions.Conventions' FromAssembliesInPath method

Related

Switch Dll Reference to different version at runtime

In my .NET Application (.NET Framework 4.8) I am trying to implement an exchange of the implementation of one of my interfaces.
I have got the following project structure:
MyProgram.Exchange:
public interface IExchange {
void DoSomething();
}
MyProgram.Exchange.V1 (Reference to Some.dll (Version 1.0.0.0))
[Export(typeof(IExchange))]
public class Exchange : IExchange {
public void DoSomething(){}
}
MyProgram.Exchange.V2 (Reference to Some.dll (Version 2.0.0.0))
[Export(typeof(IExchange))]
public class Exchange : IExchange {
public void DoSomething(){}
}
In my Startup.cs of my main program I create a DirectoryCatalog and register the Types inside my V1 as default behaviour:
var catalog = new DirectoryCatalog($".", $"*V1*.dll");
// ...
var builder = new Autofac.ContainerBuilder();
builder.RegisterComposablePartCatalog(catalog);
Initially this works fine. But at some point further inside my Application I need to switch the Reference from V1 to V2.
My Problem now is, that when calling "builder.RegisterComposablePartCatalog(catalog)" I get an exception, because the referenced assembly "Some.dll" is already registered with another version.
Is there a way to completely remove the reference to MyProgram.Exchange.V1 and all its dependencies and register the MyProgram.Exchange.V2 instead.
Sorry if the explanation of the problem isnt the best, but I hope you get my problem.
I think in the case the option is to load assemblies dynamically using custom AppDomain.
You can realize it like:
create AppDomainAppDomain.CreateDomain()
Assembly.Load()
use instance / register your types using reflection
And when you'll need to replace the dll:
remove the types from DI registration
remove the AppDomain AppDomain.Unload() (the way to UNLOAD already loaded assemblies)
load new assemlby and register types using reflection analogically.
Unfortunately, when you reference dll at compile time, there's no option to "unload" it.

Autofac in a Multi-Project Solution

I'm using Autofac as a dependency injection system in a C# solution that spans several class libraries and several executables. I'm using modules to configure Autofac, but that still leaves me with the issue of building the DI container, which varies depending upon which executable I'm composing it for.
I tried using Autofac's RegisterAssemblyModules, but you have to give it a list of assemblies to scan, and until some Type in a class library assembly is used, the assembly isn't loaded, and hence not available to scan.
Some people recommend loading every assembly in the bin directory which might have an Autofac module definition in it. But that seems to pose the risk of an undesired assembly getting slipped into action.
So what I came up with is this static class, which is defined in a common class library:
public static class Container
{
private static IContainer _instance;
private static Dictionary<string, Assembly> _moduleAssemblies = new Dictionary<string, Assembly>();
public static void RegisterAutofacModuleAssembly<T>()
where T : class
{
var assembly = typeof(T).Assembly;
if( !_moduleAssemblies.ContainsKey( assembly.FullName ) )
_moduleAssemblies.Add( assembly.FullName, assembly );
}
public static IContainer Instance
{
get
{
if( _instance == null )
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyModules( _moduleAssemblies.Select( ma => ma.Value ).ToArray() );
_instance = builder.Build();
}
return _instance;
}
}
}
You use this by including lines like this in the startup code of an application:
public static void Main(string[] args)
{
AutoFacRegistrar.Container.RegisterAutofacModuleAssembly<ScannerApp>();
AutoFacRegistrar.Container.RegisterAutofacModuleAssembly<AppConfiguration>();
Is this a reasonable solution? If there's a better, more flexible one I'd be interested in learning about it.
Issue in IOptions<> Binding System
In implementing #Nightowl888's suggestion, I ran into a problem with the Microsoft configuration IOptions<> system. Here's how I'm trying to configure Autofac to resolve AppConfiguration objects:
protected override void Load( ContainerBuilder builder )
{
base.Load( builder );
var config = new ConfigurationBuilder()
.AddJsonFile( AppConfiguration.WebJobsConfigFile, false )
.AddUserSecrets<ConfigurationModule>()
.AddEnvironmentVariables()
.Build();
builder.Register<AppConfiguration>( ( c, p ) =>
{
var retVal = new AppConfiguration( c.Resolve<ILogger>() );
config.Bind( retVal );
return retVal;
} )
.SingleInstance();
}
The problem occurs in the call to Bind(). As it traverses and parses the configuration information, it expects to create various objects thru parameterless constructors...which makes it difficult to use constructor injectcion.
If I can't use constructor injection, I need to be able to resolve against the DI container within constructor code. I don't see how I can define a library assembly which doesn't hardwire in a particular DI container's resolution semantics.
Thoughts? Other than "abandon the IOptions<> system", which I've considered, but it provides a number of benefits I'd like to maintain.
Every executable application should have its own unique DI configuration. Libraries and frameworks should be built to be DI-friendly, but not actually reference any DI container.
A composition root is an application's configuration. Sharing it between applications is similar to sharing a .config file between applications - that is, it is not usually done. See Composition Root Reuse.
If you are going to use autofac modules, they should be a part of the application that uses them, not included in the assembly with the components that are being composed. While it may seem like you are gaining something by not having to repeat the configuration code in every application, the main issue with doing this is that it means your application has lost one of the main benefits of DI - that is, it cannot provide an alternative implementation of any given component. The whole point of making a library loosely-coupled is that it allows the decision of how the application will be coupled together to ultimately be made by the application that hosts the components.
Pet peeve: Also note that how many projects or solutions you have has absolutely nothing to do with the runtime behavior of an application (such as how it is composed). Projects and solutions are a way to organize code before it is compiled - once the code is compiled, there is no concept of "project" or "solution", all you are left with are "assemblies" that may depend on other "assemblies". For each application you end up with an executable assembly and 0 or more dependent assemblies. The composition root should only exist in the executable assembly.

Dependency Registrar for other assembly

I have a simple question about dependecy registration.
I'm developing a brand new web application that use Engine Context paradigm with Autofac container. For any library on the solution I have one class implementing IDependencyRegistrar that implement a common Register method, due to add one the container some specific implementation of some interfaces and components.
In this way, a base Core library (running at application startup) provide a RegisterDependencies method that lookup on every Executing Assembly to discover all the DDL's used by the application and registering them on Autofac Container.
The code that provide this behavior is:
builder = new ContainerBuilder();
var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>();
var drInstances = new List<IDependencyRegistrar>();
foreach (var drType in drTypes)
drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType));
//sort
drInstances = drInstances.AsQueryable().OrderBy(t => t.Order).ToList();
foreach (var dependencyRegistrar in drInstances)
dependencyRegistrar.Register(builder, typeFinder, config);
builder.Update(container);
Where the FindClassOfType<IDependencyRegistrar> works thanks to a Method implementation like that:
public virtual IList<Assembly> GetAssemblies()
{
var addedAssemblyNames = new List<string>();
var assemblies = new List<Assembly>();
if (LoadAppDomainAssemblies)
AddAssembliesInAppDomain(addedAssemblyNames, assemblies);
AddConfiguredAssemblies(addedAssemblyNames, assemblies);
return assemblies;
}
And, AddAssemblyInAppDomain is:
private void AddAssembliesInAppDomain(List<string> addedAssemblyNames, List<Assembly> assemblies)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (Matches(assembly.FullName))
{
if (!addedAssemblyNames.Contains(assembly.FullName))
{
assemblies.Add(assembly);
addedAssemblyNames.Add(assembly.FullName);
}
}
}
}
The problem is: when I end up on adding in mysolution the MVC project (the front-end), I've referenced on it only direct accessing library (service layer and some infrastructure components) but no DataLayer components and some other DLL. Due to the fact that MVC not referencing directly some libraries of deep layers, my Engine Context doesn't see the others sub-components and not registering them on the Autofac container, causing a
'no registered services'
exception when execution make explicit request on them.
The whole system just works if I add reference to any library from the MVC project but, for layered architectured application, this is not a best practice: my MVC need to know nothing about DataLayer or others low-layered services.
However, in this way, no ExecutingAssembly are discovered, so, not dependency are registered anymore.
Wich is the best approch to resolve this situation without referencing all assemblies directly from main MVC project?
What you are trying to do is described in Autofac documentation as Assembly Scanning, take a look here. Basically, to get all assemblies in IIS-hosted application you need this piece of code:
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();
EDIT:
Ok, so I understand the situation is like this:
Project Web is a MVC web app.
Project Model is a class library where you have your contracts (interfaces) defined, e.g. for DAL, but also for Web.
Project DAL contains some implementations of contracts from Model.
There might be some additional class libraries, but they all uses Model for contracts.
So to sum up - all projects have reference to Model, but they have no references to each other.
I think for every library (except Model) you should create a module. To do so, create a class implementing Module type from Autofac library and override Load method - put all your module registration in there. Then, in Web app start you should load all assemblies and register their modules. But, as you mentioned, assemblies other than Web are not present in bin directory; you should copy them there "manually", for example in Post-Build action (Project Properties -> Build Events -> Post-Build action). The following command should do the work:
xcopy /Y "$(TargetDir)*.dll" "$(ProjectDir)..\{Your Web App}\bin"
Also, in your solution properties you should set, that Web project "depends" on all other projects. It would assure all other libraries would be build before Web. It does not add any reference between these assemblies.
Then, during application startup, you should search for you assemblies in bin folder and register each assembly module, like this:
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterControllers(typeof(MvcApplication).Assembly);
var libFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/bin"));
var libFiles = libFolder.GetFiles("*.dll", SearchOption.AllDirectories);
foreach (var lib in libFiles)
{
var asm = Assembly.LoadFrom(lib.FullName);
containerBuilder.RegisterAssemblyModules(asm);
}
var container = containerBuilder.Build();
You might want to add some filter to libFolder.GetFiles() to retreive only your assemblies, not all from bin.
If your other assemblies contains Mvc Controllers, you should take a look how to manage the situation here (see Initializer class). Basically, in pre-start of application you would need to add assemblies to BuildManager. Otherwise, the code above should work just fine.
If you are working on a non-web project then my answer might help?
To your Ioc class add a method i.e:
public static void SetIocForTesting(bool forUnitTesting)
{
_testContext = forUnitTesting;
}
Sample container set-up code, delegate out the responsibility of getting the assemblies to load into the builder. i.e GetModules():
public static IContainer Container
{
get
{
if (_container != null)
{
return _container;
}
var builder = new ContainerBuilder();
foreach (var lib in GetModules())
{
builder.RegisterAssemblyModules(lib);
}
_container = builder.Build();
return _container;
}
}
When scanning for Assemblies, switch on the testContext variable:
private static IEnumerable<Assembly> GetModules()
{
if (_testContext)
{
return AppDomain.CurrentDomain.GetAssemblies();
}
var currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (currentPath == null)
{
throw new NullReferenceException("Unable to build the container because currentPath variable is null.");
}
// XXXX = assign a wild card
var libFolder = new DirectoryInfo(currentPath);
var libFiles = libFolder.GetFiles("XXXX.*.dll", SearchOption.TopDirectoryOnly);
return libFiles.Select(lib => Assembly.LoadFrom(lib.FullName)).ToList();
}
When unit testing your IoC provider and a registration:
protected virtual void GivenThat()
{
IocProvider.SetIocForTesting(true);
}
.. you have a method that switches the IoC to ensure it works correctly with all assemblies referenced and loaded by your test project. The above method lives inside an abstract base class I use for BDD style unit testing.
Your test project usually ends up referencing a lot of assemblies which means resolving services have a higher success rate.
Finally, for non UnitTesting code add a static constructor:
static IocProvider()
{
_testContext = false;
}
This will ensure a default work flow for production code.
Feel free to play with the above format to suit your needs; I hope it helps someone in the way the above question and answer helped me.

Api Controller get all types that implement an interface

I know how to get all types that implement an interface such as using this code.
However I have not figured out why I can't make this work in my Asp.Net MVC ApiController. I have two projects (apologies for the naming convention. I created a solution from scratch just to make sure that my existing one was not the cause of the error):
.sln
-WebAPI
-ClassLibrary1
-Interface1
-Class1 : Interface1
WebApi has a project reference to ClassLibrary1.
Calling my ApiController it looks at the dlls in the bin directory. It is able to get ClassLibrary1.dll but when it tries to look at which type is assignable from Interface1 it does not find anything.
Code is just a .net mvc project and class library and hosted here
You don't need to find referenced assembly by its path, you can just use the type to get its assembly as below:
internal class Program
{
private static void Main(string[] args)
{
var type = typeof(Interface1);
Assembly loadedAssembly = type.Assembly;
var types = loadedAssembly.GetTypes().Where(c => type.IsAssignableFrom(c));
foreach (var typeFound in types)
{
Console.WriteLine(typeFound.Name);
}
Console.ReadKey();
}
}
Output:
Interface1
Class1
The problem is that you have the assembly ClassLibrary1 loaded twice and therefore ClassLibrary1.Interface1 from the reference is not the same interface as ClassLibrary1.Interface1 from the loaded assembly.
Move Interface1 to its own shared library and reference this shared library in both ClassLibrary1 and WebAPI to solve your problem.
About Assembly.LoadFile, this is fine if you're planning to make a plugin like system. This is not needed if you are referencing the library because then you can just enumerate the types from the already loaded assembly.
In that case you can use:
typeof(Interface1).Assembly.GetTypes().Where(c => typeof(Interface1).IsAssignableFrom(c));
as suggested by Bhushan Firake.

Custom attributes in C#

I have a custom attribute for my page like this:
[PageDefinition("My page", "~/Parts/MyPage.aspx")]
My PageDefinition looks like this, where AttributeItemDefinitions is get set for Title, Url, IsPage and IsUserControl
public class PageDefinition : AttributeItemDefinitions
{
public PageDefinition(string title, string url)
: this()
{
Title = title;
Url = Url;
}
public PageDefinition()
{
IsPage = true;
IsUserControl = false;
}
}
But i can't find any good way to add all page with that attribute to a placeholder where all links should be list with the title and url. Do you have any good idea? Thanks for your help.
When I've created such custom attributes that define some metadata on a class I've often built a small routine that scans all classes of an assembly using reflection.
In my current project I'm using a IoC framework (other story) and instead of configuring it in a custom config file I've built myself a ComponentAttribute that defines what interface a class belongs to. (From a bird's eye view: I ask the IoC framework for a interface later on and it knows how to instantiate classes that implement that and how they fit together)
To configure that IoC framework I need to call a member of a certain class and tell it which class to interface mappingts exist.
ioc.ConfigureMapping(classType, interfaceType)
To find all those mappings I use the following two methods in one of my helper classes
internal static void Configure(IoCContainer ioc, Assembly assembly)
{
foreach (var type in assembly.GetTypes())
AddToIoCIfHasComponentAttribute(type, ioc);
}
internal static void AddToIoCIfHasComponentAttribute(Type type, IoC ioc)
{
foreach (ComponentAttribute att in type.GetCustomAttributes(typeof(ComponentAttribute), false))
{
ioc.ConfigureMapping(attribute.InterfaceType, type);
}
}
What I'm doing here is enumerating all of an assemblies' types in the first method, and than evaluting the attribute in the second one.
Back to your problem:
Using a similar approach you could find all the marked classes and record them in a container (ArrayList or similar) along with all the data you have defined in the attribute (Page path, etc.).
Update (Answer to comment)
When you build your program in Visual Studio you normally have one or more projects. For each project you will get a distinct assembly (.dll or .exe file). The code above will examine all the classes within one assembly. Seen that way an assembly is a collection of collected .cs files. So you want to search an assembly, not a directory of .cs files (which are source code and not part of the running application.)
So what's probably is missing: How can you access an assembly from your code when you want to search for classes? You just take ANY class you know (that is in the assembly/project where your other classes are) and obtain the assembly it is in by calling
var assembly = typeof(MyDummyClass).Assembly;
and afterwards you'd call something you derived from the code above
AnalyzeClasses(assembly)
and AnalyzeClasses would look like
internal static void AnalyzeClasses(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
AnalzyeSingleClass(type);
}
internal static void AnalzyeSingleClass(Type type)
{
foreach (MyCustomAttribute att in type.GetCustomAttributes(typeof(MyCustomAttribute), false))
{
Console.WriteLine("Found MyCustomAttribute with property {0} on class {1}",
att.MyCustomAttributeProperty,
type);
}
}
And you'd just call all that before you run your application code, for example right
at the top in main() (for applications) or if it's difficult in advanced you can also
call this on demand when you need the collected data. (For example from an ASP.NET page)
It might be more than you need but...
I run into this pattern all of the time in my projects so I implemented a type loader that can be supplied with user defined delegates for a type search matching.
http://www.codeproject.com/KB/architecture/RuntimeTypeLoader.aspx

Categories