1

how are you??

I hope one of you can help me!!

These are the packages that come in play and their relation (I'm working with C#):

Web---->ILogic
          ^
          |
        Logic------>Ilog
                      ^
                      |
                     Log

Let's suppose that in each project there's a class with the same name. The class Logic implements the ILogic interface, and the class Log implements the ILog interface.

So these would be the classes:

public class Web{
    internal static readonly ILogic = InitializeLogic();

    private static ILogic InitializeLogic(){
        string path = ConfigurationManager.AppSettings["LogicAssemblyPath"];
        string className = ConfigurationManager.AppSettings["LogicClass"];
        Assembly assembly = Assembly.LoadFile(path);
        Type type = assembly.GetType(className);
        return (ILogic)Activator.CreateInstance(type, null);
    }
}

public class Logic{
    internal static readonly ILog = InitializeLog();

    private static ILogic InitializeLogic(){
            string path = ConfigurationManager.AppSettings["LogAssemblyPath"];
            string className = ConfigurationManager.AppSettings["LogClass"];
            Assembly assembly = Assembly.LoadFile(path);
            Type type = assembly.GetType(className);
            return (ILog)Activator.CreateInstance(type, null);
    }
}

So, when I call

Web.Method();

It instantiates the Logic class using Reflection, which at the same time instantiates the Log class using Reflection again.

This throws the next exception:

System.IO.FileNotFoundException: Could not load file or assembly 'ILog, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

This can be solved by adding a reference in Web either to ILogic or to ILog (if I add one of these references it WORKS, there are no problems with the paths in the appsetting or anything like that), but obviously, I need a solution where I don't need to add any reference.

Any help/insight will be appreciated!!

Thanks!

Natan Menoni
  • 41
  • 1
  • 6
  • The error is obvious, the Logic class need the Ilog interface (the log class is loaded by reflection). You can try to load the ilog assembly (a simple Assembly.Load) before load the log assembly. After this you may view the loaded assembly in AppDomain.CurrentDomain.GetAssemblies() – bdn02 Mar 05 '15 at 23:24
  • I see.. So before I do the second reflection, in InitializeLog in the Logic class, I have to manually load the ILog assembly?? Thanks!! – Natan Menoni Mar 05 '15 at 23:46
  • Yes, or you may try to "patch" the error in AppDomain.CurrentDomain.TypeResolve event – bdn02 Mar 06 '15 at 00:00
  • Mmm, ok. I tried adding the manual load to the Logic class, but it doesn't even reach that. So I added the manual load of the ILog assembly on the Web class, like this: `string path = ConfigurationManager.AppSettings["AssemblyILog"]; Assembly.LoadFile(path); var assemblies = AppDomain.CurrentDomain.GetAssemblies();` The assembly was effectivelly loaded into the AppDomain, but then when it tried to instantiate the Logic class it failed again... I don't know much about the AppDomain, but I guess there's only one in my case. I'll investigate that TypeResolve event, thanks a lot man!! – Natan Menoni Mar 06 '15 at 00:06
  • Same error on ilog assembly? – bdn02 Mar 06 '15 at 08:38
  • You refer to this dynamic loading of dependencies as "Dependency Inversion", but I would urge you to read Robert Martin's "Agile Principles, Patterns, and Practices". He explains that the startup project of your application can, may, and will always refer to ALL other assemblies in the project. There is nothing wrong with that. Also read this: https://stackoverflow.com/questions/9501604/ioc-di-why-do-i-have-to-reference-all-layers-assemblies-in-entry-application – Steven Mar 06 '15 at 09:57
  • Well, I call it dependency inversion because instead of Web depending on Logic, they both depend on the ILogic package, that will not change, while it's implementation can change. Despite that, what you say makes a lot of sense, that link was very interesting (thanks!!), and it's probably the way things should be done in larger projects, but this project is only for college, it doesn't even have 10 c# projects, and we were told to do it as loosely coupled as possible. Thanks again! – Natan Menoni Mar 06 '15 at 17:03
  • It's good to practice loosely coupled code in college, even if the size and the lifetime of the project does not justify it. However, dynamically loading assemblies to prevent letting the startup project taking a reference on other projects is almost never needed. So you are taking the concept of loosely coupled code a step too far. Do note that even though your assemblies reference each other, the classes are still loosely coupled. – Steven Mar 09 '15 at 14:28

1 Answers1

3

So, I managed to solve it.

As bdn02 said, the ILog assembly was not loaded. I tried loading it manually into the Appdomain, but the strange thing was that it looked for the assembly twice:

-The first time searching by the full name 'ILog, Version=1.0.0.0, etc...'

-The second time searching only by the package name 'ILog'

Obviously, it wasn't finding it the second time and it threw the exception.

I found a workaround using the AssemblyResolve event that bdn02 suggested:

AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;

private static Assembly AssemblyResolve(object sender, ResolveEventArgs args){
        if (!args.Name.Contains("ILog"))
            return AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
        else
        {
            var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName.Contains(args.Name));
            if (assembly == null)
            {
                string rutaAssemblyILog = ConfigurationManager.AppSettings["AssemblyILog"];
                Assembly.LoadFile(rutaAssemblyILog);
            }
        }
        return AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName.Contains(args.Name));
}

The first if is for making sure that the event occured because of the ILog assembly. With this, it worked fine.

Thanks for your help!

Natan Menoni
  • 41
  • 1
  • 6