6

UPDATE 10:29 MST on 11/7

Oddly enough if I move the attribute out of the ComponentModel folder back into the root of the Common project the code works fine. I can't imagine what's possibly referencing the old namespace after all of the references were refactored for the InflowHealth.Common.ComponentModel namespace.

It's almost like there is some reference hiding somewhere that's not code-based but rather runtime and dynamic, but I sure don't see it when looking through all find results of InflowHealthErrorContext.

UPDATE 19:33 MST on 11/6

Of new interest, when I commented out the line to use the custom attribute to inherit routes, and use the default one, it still blew up. Of further interest, the namespace it's looking for InflowHealth.Common.InflowHealthErrorContextAttribute is actually the old FQN before I refactored it and moved it to a folder (and namespace) ComponentModel.

UPDATE 07:42 MST on 11/6

I believe I've identified that the issue is related to another custom attribute I'm using to inherit actions. This attribute is added to the HttpConfiguration like this:

public static void MapInheritedAttributeRoutes(this HttpConfiguration config)
{
    config.MapHttpAttributeRoutes(new InheritanceDirectRouteProvider());
}

The implementation of that attribute is pretty simple:

public class InheritanceDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
    {
        return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(true);
    }
}

It appears that inheriting this InflowHealthErrorContext attribute is causing issues, but I'm not sure exactly what the issue is. I've tried:

  • Removing Inherited = false so that it is inheritable.
  • Removing AllowMultiple = true just because that was misconfigured.

Those did not change the error.


ORIGINAL POST

I have a very simple attribute in a Common assembly shared by a couple Web API applications. As simple as this is I just can't figure out what would cause this exception.

I have tried to collect Fusion logs on this, but it's not logging them.

This is the Attribute:

using System;

namespace InflowHealth.Common.ComponentModel
{
    [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
    public sealed class InflowHealthErrorContextAttribute : Attribute
    {
        // This is a positional argument
        public InflowHealthErrorContextAttribute(string errorContext)
        {
            ErrorContext = errorContext;
        }

        public string ErrorContext { get; }
    }
}

This would be used on a route to later provide some extra context to the automated error logging done inside of a filter:

[Authorize(Roles = Roles.ALL_ADMINS)]
[Route("api/ControlPanelApi/PayerClassifications")]
[InflowHealthErrorContext("Error getting payer classifications.")]
public IHttpActionResult GetPayerClassifications(int clientId, bool showAllRows)
{
    return Ok(GetData(payerClassificationManager, clientId, showAllRows));
}

Upon loading the application, when Web API Routes are registered, it fails. Here is the line it's breaking on:

GlobalConfiguration.Configure(WebApiConfig.Register);

It's throwing this exception:

Could not load type 'InflowHealth.Common.InflowHealthErrorContextAttribute' from assembly 'InflowHealth.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

This is the stack trace:

   at System.ModuleHandle.ResolveType(RuntimeModule module, Int32 typeToken, IntPtr* typeInstArgs, Int32 typeInstCount, IntPtr* methodInstArgs, Int32 methodInstCount, ObjectHandleOnStack type)
   at System.ModuleHandle.ResolveTypeHandleInternal(RuntimeModule module, Int32 typeToken, RuntimeTypeHandle[] typeInstantiationContext, RuntimeTypeHandle[] methodInstantiationContext)
   at System.Reflection.RuntimeModule.ResolveType(Int32 metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
   at System.Reflection.CustomAttribute.FilterCustomAttributeRecord(CustomAttributeRecord caRecord, MetadataImport scope, Assembly& lastAptcaOkAssembly, RuntimeModule decoratedModule, MetadataToken decoratedToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, Object[] attributes, IList derivedAttributes, RuntimeType& attributeType, IRuntimeMethodInfo& ctor, Boolean& ctorHasParameters, Boolean& isVarArg)
   at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType, Boolean mustBeInheritable, IList derivedAttributes, Boolean isDecoratedTargetSecurityTransparent)
   at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeMethodInfo method, RuntimeType caType, Boolean inherit)
   at System.Reflection.RuntimeMethodInfo.GetCustomAttributes(Type attributeType, Boolean inherit)
   at System.Attribute.GetCustomAttributes(MemberInfo element, Type type, Boolean inherit)
   at System.Attribute.GetCustomAttribute(MemberInfo element, Type attributeType, Boolean inherit)
   at System.Reflection.CustomAttributeExtensions.GetCustomAttribute[T](MemberInfo element)
   at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.IsValidActionMethod(MethodInfo methodInfo)
   at System.Array.FindAll[T](T[] array, Predicate`1 match)
   at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem..ctor(HttpControllerDescriptor controllerDescriptor)
   at System.Web.Http.Controllers.ApiControllerActionSelector.GetInternalSelector(HttpControllerDescriptor controllerDescriptor)
   at System.Web.Http.Controllers.ApiControllerActionSelector.GetActionMapping(HttpControllerDescriptor controllerDescriptor)
   at System.Web.Http.Routing.AttributeRoutingMapper.AddRouteEntries(SubRouteCollection collector, HttpConfiguration configuration, IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider)
   at System.Web.Http.Routing.AttributeRoutingMapper.<>c__DisplayClass2.<>c__DisplayClass4.<MapAttributeRoutes>b__1()
   at System.Web.Http.Routing.RouteCollectionRoute.EnsureInitialized(Func`1 initializer)
   at System.Web.Http.Routing.AttributeRoutingMapper.<>c__DisplayClass2.<MapAttributeRoutes>b__0(HttpConfiguration config)
   at System.Web.Http.HttpConfiguration.EnsureInitialized()
   at System.Web.Http.GlobalConfiguration.Configure(Action`1 configurationCallback)
   at InflowHealthPortal.MvcApplication.Application_Start() in Global.asax.cs:line 22
Mike Perrenoud
  • 66,820
  • 29
  • 157
  • 232
  • Is the `GetPayerClassifications` method in the same namespace and/or project as `InflowHealth.Common.ComponentModel`? If they are in separate projects, then what framework are they using? – Svek Nov 06 '17 at 03:01
  • @Svek they are in separate projects. Both of them are targeting .NET 4.5 and the assembly with `GetPayerClassifications` is referencing via a project reference the `Common` assembly that the attribute is in. – Mike Perrenoud Nov 06 '17 at 03:05
  • Check if the .dll is present in the output folder when you build your MVC app; if it is not, change properties to `Copy always` – zaitsman Nov 06 '17 at 04:18
  • @zaitsman yep it's getting dropped in the output folder. – Mike Perrenoud Nov 06 '17 at 04:21
  • @MikePerrenoud use this tool: https://assemblyinformation.codeplex.com/ on the resulting dll to check the strong name of the assembly (it is still called strong name even when the keyinfo is null) – zaitsman Nov 06 '17 at 04:23
  • If you right-click each project, do they both have the option to `Edit **.csproj`? – Svek Nov 06 '17 at 04:57
  • 1 check the projects for 'InflowHealthErrorContextAttribute ' and for webapi are for the same CPU type in project properties page. 2 check if there is old version of dll in GAC. – Yang You Nov 06 '17 at 12:08
  • @yyou yep same CPU architecture and this assembly was never installed in the GAC. – Mike Perrenoud Nov 06 '17 at 13:39
  • I would inspect all projects that reference Common if they reference correct version (by inspecting full path of reference). Alternative is change all `[InflowHealthErrorContext]` to `[InflowHealth.Common.ComponentModel.InflowHealthErrorContext]` and rebuild whole solution. That rebuild will fail if some project still references old Common version (for example from Release folder while you made your change in Debug). – Evk Nov 08 '17 at 12:48
  • @Evk I will give that a shot. – Mike Perrenoud Nov 08 '17 at 13:59
  • Try typeof(yourType).Assembly.CodeBase ,something like https://stackoverflow.com/questions/52797/how-do-i-get-the-path-of-the-assembly-the-code-is-in and if there is indeed on old copy referenced,delete it – George Vovos Nov 08 '17 at 14:17
  • 3
    @MikePerrenoud delete `bin` and `obj` folders and rebuild. A stray dll could be hanging around. Also are you able to provide a [mcve] that can be used to reproduce the problem? I've tried making a minimal project with what you have provided so far but am unable to get a similar error. – Nkosi Nov 09 '17 at 07:45
  • 1
    I am with @Nkosi - I suggest running ProcMonitor and seeing which DLL visual studio is *actually* loading, probably from some old bin or a old reference. – Jeremy Thompson Nov 09 '17 at 22:39
  • is the same DLL referenced in other projects, possibly via NuGet? – madoxdev Nov 13 '17 at 13:40
  • @Nkosi deleting the `bin`, `obj`, and `packages` folders and then performing the refactoring again did fix it. Please add this as an answer and I will award you the bounty. – Mike Perrenoud Nov 13 '17 at 15:30

3 Answers3

1

InflowHealthErrorContextAttribute belongs to InflowHealth.Common.ComponentModel namespace however error

Could not load type 'InflowHealth.Common.InflowHealthErrorContextAttribute' from assembly 'InflowHealth.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

shows that it's searched in InflowHealth.Common namespace. Couldn't it be an issue?

Probably in some outdated version of Common assembly that you referenced, attribute is in InflowHealth.Common namespace and code compiles without errors, but in assembly that gets to output folder attribute is actually in InflowHealth.Common.ComponentModel namespace and search of InflowHealth.Common.InflowHealthErrorContextAttribute type fails.

CodeFuller
  • 30,317
  • 3
  • 63
  • 79
  • Except when all references to `InflowHealth.Common` are project references, and all usages of the attribute build, where else could this reference be blowing up? Like I mentioned in my latest update, I too am convinced it's some runtime binding, but where? I don't see any `Web.config` references for example. That's what I was looking for too. – Mike Perrenoud Nov 08 '17 at 08:56
  • 1
    Could you check Common assembly with some disassembler, whether the attribute is actually in InflowHealth.Common namespace? I believe it will be so. Then the problem is that some outdated version of this assembly gets to output folder. Are you sure correct version of this assembly is copied to output folder when build happens? – CodeFuller Nov 08 '17 at 09:04
1

I would suggest you delete the bin and obj folders and rebuild the project.

This issue sometimes happened when there was a stray dll hanging around that was not overwritten when the projects were compiled. Usually when renaming output files.

Performing the above actions have worked for me in the past when it came to references not reflecting the changes made to code and recompilation.

The same sometimes happened with Nuget packages and this would result in having to remove the package and re-installing it in order for the correct dlls to be referenced.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
0

There are two possible causes for this:

Either the Web.Config is pointing a wrong assembly or the assembly was compiled wrong. First of all, you are pointing the wrong assembly:

'InflowHealth.Common.InflowHealthErrorContextAttribute' from assembly 'InflowHealth.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

Should point to:

InflowHealth.Common.InflowHealthErrorContextAttribute' from assembly 'InflowHealth.Common.ComponentModel

Try check this section of the web.Config:

 <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>

Or search the project for where the assembly location is set.

If that doesn't come in handy, disassemble your code and see where it leads.

Barr J
  • 10,636
  • 1
  • 28
  • 46