Search code examples
c#interfaceparameter-passingninjectguid

C# Guid method parameter being borked (changed) when calling a method


I've got a fun one here. I'm calling a method with a guid parameter and somewhere between the invocation of the call and the first step into the called method, said guid parameter is being changed.

And before anyone asks for a code sample, this one is WAY too complex to try to do a simple variant - why, you ask? Simple, if this was a simple problem to recreate (and, yes, I did create an entire solution with just the isolated conditions re-created as closely as possible as a test to see if it is, indeed, a simple to recreate problem), the folks that write the compiler would have fixed this before it ever went into production. This one is deep in the bowels of the environment and I want to know if anyone has ever seen it before and, if so, how they got 'round it.

And if not, then the Microsoft Compiler folks will want to take a look at this one and fix it, 'cause this one is bad, really bad.

Here is the issue: If I have a method call like this:

Guid aGuid = Guid.NewGuid();
List<SomeClass> vList = _Handler.SomeMethodThatGeneratesTheList(aGuid)(*1);

and somewhere else is the code (in a different project that was brought in via Nuget):

public List<SomeClass> SomeMethodThatGeneratesTheList(Guid aGuid)
(*2){
  // Do Some handling
}

When the code is invoked at point (*1), the guid parameter is just fine. With nothing in between that invocation and entry at the entry point (*2) (literally at the entry point of the method, no processing has taken place just yet, we are at the very start of the method), the parameter aGuid has changed from something like this:

{c8f09e25-9779-431e-8086-a80400c13bec}

to something like this:

{1518dcc0-0000-0000-0000-000000000000}

And oddly enough, as I re-run the test, the substituted value changes but in what is seemingly an incremental fashion e.g.

{145de090-0000-0000-0000-000000000000}

Only the first segment changes, all the rest remain zeroed out.

The actual NUnit unit test code looks like this:

    [Test]
    public void TestHandleDbmetaDataDapperResultSet()
    {
        // Arrange

        // Act
        List<DbMetaDataTableOrView> vResult =
            _DbMetaDataContext
                .HandlerDbMetaDataTableOrViewInfo
                .FetchAllTableInfoAndTranslate
                    (_CodeGenParams.DatabaseInfoId);

        // Assert
        vResult.Should().NotBeNull();
    }

Update 2: The updated code to remove the possibility that the thing that supplies the parameter value is somehow hosed looks like this:

        [Test]
    public void TestHandleDbmetaDataDapperResultSet()
    {
        // Arrange
        Guid vTestParam = Guid.NewGuid();

        // Act
        //List<DbMetaDataTableOrView> vResult =
        //  _DbMetaDataContext
        //      .HandlerDbMetaDataTableOrViewInfo
        //      .FetchAllTableInfoAndTranslate(_CodeGenParams.DatabaseInfoId);
        List<DbMetaDataTableOrView> vResult =
            _DbMetaDataContext
                .HandlerDbMetaDataTableOrViewInfo
                .FetchAllTableInfoAndTranslate(vTestParam);

        // Assert
        vResult.Should().NotBeNull();
    }

The starting value: {c86c90ef-a284-470f-8949-36eacbd74afe} The parameter when it enters the called code: {13a6d9d0-0000-0000-0000-000000000000}.

Even changing the line that generates the guid to this

            Guid vTestParam = Guid.Parse("c86c90ef-a284-470f-8949-36eacbd74afe");

yields this on the first run: {1428e040-0000-0000-0000-000000000000} and this on the second: {1457d9a0-0000-0000-0000-000000000000}

where _CodeGenParams.DatabaseInfoId contains the requisite Guid value. The signature of the called method is simply:

        public List<DbMetaDataTableOrView> FetchAllTableInfoAndTranslate
        (Guid aDatabaseId)

Update (Per request, the entire content of the called method is supplied thusly:)

        public List<DbMetaDataTableOrView> FetchAllTableInfoAndTranslate
        (Guid aDatabaseId)
    {
        List<TableInfo> vTableList = FetchAllTableInfo(aDatabaseId);
        List<DbMetaDataTableOrView> vResult = new List<DbMetaDataTableOrView>();
        DbMetaDataTableOrView vDbTable;
        foreach (TableInfo vTable in vTableList)
        {
            vDbTable =
                vTable.DbMetaData.DeSerialize<DbMetaDataTableOrView>
                    (vTable.DbMetaDataDotNetType);
            vResult.Add(vDbTable);
        }
        return vResult;
    }

Put a break point on the first brace as you enter the code and the parameter has already been changed.

And the modification takes place at the click of a single-step, as I said earlier, literally a single step between the invocation and the entry into the code.

Some of the rest of the details:

The method being invoked lives in a separate class, implemented in a separate project (.csproj) that has been stored as a nuget package. All gadgets here are current: VS 2017 Enterprise, current nuget.exe, .NET framework 4.7.

The calling unit test has inherited the called nuget via the reference to the project under test, which acquired the nuget package in the usual (nuget) way. The package itself exposes the called class methods as part of an interface such that _DbMetaDataContext is an implementation of IDbMetaDataContext and that is true of all the ancestral classes. All methods are exposed as implementations of their respective interfaces. This is done to ensure that DI works as desired and the current DI engine is Ninject. All DI-based unit tests function just fine up to this point.

I have yet to try this with another DI library, I've used Ninject for so long with no issues that I never bothered to look at another library. Am going to now just to remove that as a possible source of difficulty. Will check out SimpleInjector next.

It'll take awhile because the methods above in question are at the top of a fairly large food chain in that they are the end-result of a lot of underlying library code (hence the inability to supply a "simple" way to reproduce this). The method under test is the only method that fails in the class, all of the other methods in the class pass their unit tests just fine, however, none of them take a Guid as a parameter. String passing, etc. just works. From this I conclude that it isn't the class that's a problem, nor the inheritance chain, nor the use of interfaces that is the problem, it's just the handling of a Guid parameter in that environment that's the problem. As to what does the handling, that's why I'm going to try to take DI out of the picture (not a simple task) as it might be the way that Ninject does the binding between interface and concrete class. I have a lot of static methods that perform the binding chain for me so I don't have to remember all the bindings when I finally get to where I want to use the final gadget. That methodology has worked just fine up to this point, the unit tests using the binding calls all work in a DI environment, it's just the last set that seems to be introducing this behavior.

I'll edit this as I learn more. Here is the Call Stack when the method is entered:

enter image description here


Solution

  • Strangest thing I have ever seen. Change the parameter type from standard "Guid aParam" to "ref Guid aParam" and it works just fine now.

    Update: false success. Seems it also depends on when you have rebooted your system. I am still able to generate the problem and, after even further investigation, have concluded that this might be a debugger/ReSharper issue in that I can now create a case using simple code that replicates the same problem. Once need only create a separate solution, add a package with a class that implements an interface, add a different interface from some other simple class, turn the whole thing into a NuGet package, create a second solution, create a simple class that uses in instance of the class in the NuGet package and start single stepping. Fails there, or at least the debugger fails to show the correct value when stepping through the code.