NickCraver/StackExchange.Exceptional

Load StackExchange.Exceptional dynamically

Duxez opened this issue · 1 comments

Duxez commented

For a project at my company I am making a package that has to be installed to a bunch of different websites. We want to get errors from error loggers if they are installed. And are currently willing to support Elmah and Exceptional.

My boss however, also wants to only distribute 1 package for 3 different possibilities, these being:

  1. Website with exception logger installed
  2. Website with StackExchange.Exceptional installed
  3. Website with Elmah installed

To make these possible I'm trying to dynamically load the dll's of these logger packages and invoke methods needed or get the parameter.

This working perfectly fine for Elmah by doing for example the following:

Assembly assembly = Assembly.LoadFile(HostingEnvironment.MapPath("~/bin/Elmah.dll"));

    Type type = assembly.GetType("Elmah.ErrorLog");

    object[] parameters = new object[1];
    parameters[0] = null;

    var customResult = type.InvokeMember("GetDefault", BindingFlags.InvokeMethod, null, type, parameters);

    Type myType = customResult.GetType();
    IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());

    string result = "";

    foreach (PropertyInfo prop in props)
    {
        if (prop.Name == "Name")
            result = prop.GetValue(customResult, null).ToString();
    }

Which right now simply gets the name of the default ErrorLog

Then getting to Exceptional and trying a similar approach:

Assembly assembly = Assembly.LoadFile(HostingEnvironment.MapPath("~/bin/StackExchange.Exceptional.dll"));

    Type type = assembly.GetType("StackExchange.Exceptional.ExceptionalModule");

    object objectInstance = Activator.CreateInstance(type);

    PropertyInfo info = type.GetProperty("ErrorStore");
    foreach(PropertyInfo property in type.GetProperties())
    {
        LogHelper.Info<Exceptional>(property.Name + ": " + property.GetValue(objectInstance, null));
    }

This piece of code is to try and get the name of the ErrorStore of the Default ErrorStore used. With some logging for testing and such.

However the moment it has to do anything with the "Default" parameter of the "ErrorStore" class which is supposed to return the default ErrorStore. I always get an exception thrown:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentOutOfRangeException: ErrorStore 'type' must be specified
    Parameternaam: settings
    bij StackExchange.Exceptional.ErrorStore.GetFromSettings(ErrorStoreSettings settings)
    bij StackExchange.Exceptional.ErrorStore.GetErrorStoreFromConfig()
    bij StackExchange.Exceptional.ErrorStore.get_Default()
    bij StackExchange.Exceptional.ExceptionalModule.get_ErrorStore()
    --- Einde van intern uitzonderingsstackpad ---
    bij System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
    bij System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj,   Object[] parameters, Object[] arguments)
    bij System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
    bij System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
    bij System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
    bij h5ysr_package.Exceptions.Exceptional.ExceptionalErrors()
    bij h5ysr_package.DataCollectorSender.<StartCollection>d__3.MoveNext()

Which brought me to the part of the code that is throwing the error:

if (settings.Type.IsNullOrEmpty())
            throw new ArgumentOutOfRangeException(nameof(settings), "ErrorStore 'type' must be specified");

This being part of the Exceptional package and therefore accessible by me via code.

Is there anyway to make sure type gets specified when loading the StackExchange.Exceptional dll dynamically? Or is just not doable?

I'll admit this really hadn't occurred to me to even try...have you tried explicitly loading the Postgres DLL ahead of the main library, so the types are available in memory? This is only half the issue...I'm not really sure how the settings behavior is going to play there, but will try to poke over the break here.