Skip to content
This repository has been archived by the owner on Jan 15, 2021. It is now read-only.

Tab disappears #462

Closed
leonardlazzari opened this issue Jul 17, 2013 · 18 comments
Closed

Tab disappears #462

leonardlazzari opened this issue Jul 17, 2013 · 18 comments
Assignees
Milestone

Comments

@leonardlazzari
Copy link

I am struggling with a very strange problem

On an application in QA testing (ASP.NET MVC3) I installed Glimpse DLLs and made the documentation configurations

Switch On Glimpse and in the suite see all the TAB menu

all works fine.

If I make a recycle AppPool for application, in the Glimpse menu some TAB disappear (Execution, Metadata and Model.Binding)

the rest of suite works normally.

After made an identical copy of the glimpse libraries (this force the app restarting ) all tabs reappear

application is the MVC 3, the dll GLIMPSE were added with NuGet

Glimpse.Core 1.4.2
Glimpse.AspNet 1.3.1
Glimpse.MV3 1.3.2

I also tried to update to version 1.5.0 Glimpse.Core but having same behavior

Any idea?

Thanks for your attention

@CGijbels
Copy link
Collaborator

I've seen the same issue being mentioned on stackoverflow.com

I'll paste my answer over here as well, so we can continue the discussion and look for possible solutions over here (although I have to say it is kind of strange nobody noticed before, maybe that is because Glimpse is used most of the time in a development environment I guess?)

Anyway:

The reason seems to be ASP.Net and the way it loads assemblies from your application directory for the first time and after a recycle. On first start, the assemblies are loaded from your application's bin directory and after a recycle (or even an IIS restart) it loads the assemblies from the Temporary ASP.NET Files directory. And depending on the situation more or less assemblies are loaded.

You can see this for yourself by adding the following lines to your Application_Start method in your Global.asax

protected void Application_Start()
{
    Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
    bool glimpseMvc3AssemblyLoaded = loadedAssemblies.Any(a => a.FullName.Contains("Glimpse.Mvc3"));
    File.AppendAllText(
        "C:\\temp\\output.txt",
        string.Format(
            "{0}{1} assemblies loaded and Glimpse.Mvc3 is{2} one of them",
            Environment.NewLine,
            loadedAssemblies.Length,
            glimpseMvc3AssemblyLoaded ? string.Empty : " not"));
}

If you run that then you will see the following entries in your output.txt file (although numbers may differ)

On first initial start:

60 assemblies loaded and Glimpse.Mvc3 is one of them

After recycles

30 assemblies loaded and Glimpse.Mvc3 is not one of them

30 assemblies loaded and Glimpse.Mvc3 is not one of them

After removing the corresponding directory inside the Temporary ASP.NET Files directory

70 assemblies loaded and Glimpse.Mvc3 is one of them

How does this correlate with Glimpse. Well Glimpse makes a call to AppDomain.Current.GetAssemblies() after which it will look for types that implement ITab, but if the Glimpse.Mvc3 assembly isn't returned then tabs defined inside of it won't be discovered and hence not shown.

So the question is how can we make sure the assembly is loaded after a recycle. I was thinking of using something like WebActivator, which adds a new dependency, but shouldn't be an issue for an Mvc3 specific assembly?
On the other hand I think we still want to prevent the user from adding Glimpse specific code to its code base?

Other thoughts? Am I maybe missing something?

@rbbrk
Copy link

rbbrk commented Jul 17, 2013

That sounds like exactly what's happening here.

Is the way DLLs are selected/ignored from Temporary ASP.NET Files during AppPool Recycles documented?

@avanderhoorn
Copy link
Member

As a note, extra details on this issue can be found here - https://groups.google.com/forum/#!topic/getglimpse-dev/V2ec8Pc9AHg.

@davidebbo
Copy link

Generally, you should avoid relying on AppDomain.Current.GetAssemblies(), as there is no guarantee that things will be loaded at the time you call it, even if they're in bin.

Instead, please use BuildManager.GetReferencedAssemblies().

@avanderhoorn
Copy link
Member

@avanderhoorn
Copy link
Member

@CGijbels Since you have a repo on this one, do you think you could make the change and test it out?

@CGijbels
Copy link
Collaborator

@avanderhoorn @davidebbo Will that work as well for assemblies that are not referenced like Glimpse.Mvc3 ? I mean there is no reference in the web.config and no compilation dependency

@davidebbo
Copy link

It'll work as long as it's in bin.

@CGijbels
Copy link
Collaborator

@avanderhoorn I'll do

But I've got some small remarks:

  • The BuildManager is part of System.Web.Compilation and since the code we need to patch is part of Glimpse.Core which has no reference to System.Web, is this the way to go then? I mean the same change might need to be applied at other places as well?
  • There used to be a DiscoveryLocation feature that is supposed to be removed with v2, unfortunately that feature seems to be taken the shadow directory in consideration as well.

Maybe @nikmd23 has some thoughts on this DiscoveryLocation ?

@CGijbels
Copy link
Collaborator

To inform you that BuildManager.GetReferencedAssemblies() does indeed fix the issue.

@avanderhoorn @nikmd23 there is one thing that we could "abuse" and that is the fact that if you call BuildManager.GetReferencedAssemblies() first and after that AppDomain.CurrentDomain.GetAssemblies() then the latter will find the Glimpse.Mvc3 assembly because the former loaded it into memory.

Therefore I would suggest an approach that has less impact and that is to have the Glimpse.AspNet.HttpModule make the call to BuildManager.GetReferencedAssemblies() in its static constructor. Since this module is responsible to initialize and start the Glimpse runtime in the context of AspNet, it can make sure that it does what needs to be done to have the runtime work as appropriate, hence preloading all the assemblies.

This will solve the additional dependency on System.Web that we would have to make from Glimpse.Core and prevents the fact that we need to look for all other uses of AppDomain.CurrentDomain.GetAssemblies()

It's a one liner to solve this issue. What do you think?

@CGijbels
Copy link
Collaborator

@rbbrk @leonardlazzari In the meanwhile you could do the same from within your Gobal.asax just call BuildManager.GetReferencedAssemblies() and you guys are unblocked and no dependency on Glimpse types in your application code. Maybe add a comment so that it can be removed once a fix has been released ;-)

Could you try that to make sure it works?

@avanderhoorn
Copy link
Member

Humm that kinda sucks. Might be more than just a quick fix. But I like the idea of calling BuildManager.GetReferencedAssemblies() first and after that AppDomain.CurrentDomain.GetAssemblies().

The problem with AppDomain.CurrentDomain.GetAssemblies() not picking up everything seems to be a problem due to the way ASP.NET operates rather than .NET in general. This means that the AppDomain.CurrentDomain.GetAssemblies() approach should work in cases where Glimpse is running inside of NancyFX or some other non ASP.NET stack.

@davidebbo all being equal are you able to confirm wither AppDomain.CurrentDomain.GetAssemblies() run inside a selfhosted situation (thinking webapi) would work?

@CGijbels
Copy link
Collaborator

The problem with AppDomain.CurrentDomain.GetAssemblies() not picking up everything seems to be a problem due to the way ASP.NET operates rather than .NET in general. This means that the AppDomain.CurrentDomain.GetAssemblies() approach should work in cases where Glimpse is running inside of NancyFX or some other non ASP.NET stack.

I'm not sure if that is related to AppDomain.CurrentDomain.GetAssemblies(), since GetAssemblies effectively only returns assemblies that were loaded into the app domain when needed. In case of AspNet it seems to load all assemblies available inside the bin directory the first time, most likely as part of the shadow copy feature?

So if self hosts actually load all assemblies from the run directory into memory, whether they are directly needed or not, then it might work otherwise the same issue might be there. Glimpse.Core and Glimpse.AspNet are loaded because the Glimpse.AspNet.HttpModule is defined in the web.config, otherwise it wouldn't be loaded either.

I'm even not sure if BuildManager.GetReferencedAssemblies() would work outside the context of AspNet, but I guess @davidebbo can confirm or deny that.

@leonardlazzari
Copy link
Author

Hi everyone !

Thank you for your attention!

I made a change and adding BuildManager.GetReferencedAssemblies () in my Global.asax.
Now the behavior of TABS is correct, Glimpse.MVC3 is loaded properly after apppool reciclyng

Leo

@CGijbels
Copy link
Collaborator

@leonardlazzari Alright, glad to hear the workaround works for you

@davidebbo
Copy link

AppDomain.CurrentDomain.GetAssemblies just returns what's already been loaded. So whether it'll do the right thing for you depends on whether the framework you live in guarantees that all relevant assemblies are loaded before you call it. In the general case where components are loaded dynamically, it's a pretty dangerous assumption.

I think the workaround of calling BuildManager.GetReferencedAssemblies early on is fine (slightly hacky but oh well).

And BuildManager.GetReferencedAssemblies while not work outside of an ASP.NET hosted app domain.

@CGijbels
Copy link
Collaborator

@davidebbo thanks for confirming our assumptions

@avanderhoorn are you OK then if we go for the hacky approach? //cc: @nikmd23

@nikmd23
Copy link
Member

nikmd23 commented Jul 29, 2013

I think the "hacky" approach is fine.

@ghost ghost assigned CGijbels Jul 31, 2013
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants