Object becomes unreadable when loaded out of other assembly - asp.net

Yesterday I found myself dealing with the following problem:
Wierd JSON result - Elfinder.NET initialization error . The solution that questions suggests did not work for me though.
After significant stepping through code I discovered the JsonResult object was being put together just fine, but when my controller fetched it from the class in the elFinder assembly that put it together, my debugger did not seem to know it anymore. All it could show me was the name of the underlying type; none of the properties showed. I verified that
System.Web.MVC assembly versions matched
Newtonsoft Json.net assembly versions matched
bitness matched
Eventually I moved all of the classes in the elFinder assembly into the main web assembly, and everything was peachy keen.
So, what causes an object to lose all of its state (or makes its state unreadable) without any compiler errors or runtime errors or, well, anything? How could I have fixed this without making everything one big assembly?

Related

How to determine root cause for ReflectionTypeLoadException?

When I load our big project in a certain context, a ReflectionTypeLoadException is thrown. Its message in Windows event log reads:
Description: The process was terminated due to an unhandled exception.
Exception Info: System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types.
Could not load file or assembly 'MyFile, Version=1.2.3.0, Culture=neutral, PublicKeyToken=...'. The system cannot find the file specified.
(Assembly name MyFile, version, and PublicKeyToken are substituted/masked here.)
Now, obviously this informs me that an assembly named MyFile is not present in the folder with my binaries, and indeed it is not. If I add the assembly, everything works.
My issue is: It should not be necessary to add MyFile there. Thus, the information which file cannot be loaded is good to know, but my actual question is, which type from which file requires the loading of MyFile? For context, we are talking about an application consisting of roughly 1,500 DLL files (some 500 of which are loaded in a loop using reflection at the place where the error occurs, which will in turn reference all the others) with hundreds of types each, so scanning them manually for the offending reference using ILSpy or the like will take a lot of time.
The specific difficulty I am facing (and which I can reproduce by provoking a ReflectionTypeLoadException by inheritance as described in another answer) is that ReflectionTypeLoadException itself does not seem to contain any useful information to this end. Its Types property contains just a null entry (presumably, because the problematic type cannot be loaded precisely because of that exception). Likewise, I cannot enumerate the problematic type with ExportedTypes or even with DefinedTypes (which returns an ITypeInfo rather than a Type instance), because loading of the problematic type fails.
Is there a way to find out directly from a ReflectionTypeLoadException what was the "reference path" that led to the attempt to load MyFile? (The cumbersome alternative would probably be to intercept assembly resolution and log that, then figure out step by step which reference path led to the issue.)

System.Drawing namespace can't be used in Web Service

I've put together a c# console app that runs a web service intending to download a simple pdf document. I can return the object however I can't seem to be able to figure out how to save the pdf object to file.
ServiceName.ImageResponse responseObject = Response.GetDocument(12345);
MemoryStream ms = new MemoryStream(responseObject.Document);
//System.Drawing is the problem (namespace can't be used in a Web Service)
System.Drawing.Image Img = System.Drawing.Image.FromStream(ms);
Img.Save("c:\\saved.JPG");
This is not compiling as I'm receiving (a red squiqly line under System.Drawing and) the following error:
Error 1 The type or namespace name 'Drawing' does not exist in the namespace 'System' (are you missing an assembly reference?)
http://msdn.microsoft.com/en-us/library/system.drawing.aspx states the following:
Caution noteCaution
Classes within the System.Drawing namespace are not supported for use within a Windows or ASP.NET service. Attempting to use these classes from within one of these application types may produce unexpected problems, such as diminished service performance and run-time exceptions. For a supported alternative, see Windows Imaging Components.
I would simply like to know if there is another way. I can't seem to figure it out.
trebuchet is probably correct. Check to make sure that you have actually done the work of adding a reference to the required dll. Namespaces do not have a 1 to 1 correspondence to .dll files, for what it is worth.
As to this:
Classes within the System.Drawing namespace are not supported for use
within a Windows or ASP.NET service. Attempting to use these classes
from within one of these application types may produce unexpected
problems, such as diminished service performance and run-time
exceptions. For a supported alternative, see Windows Imaging
Components.
I don't know for certain, but my guess is that several of the components within the System.Drawing namespace expect the process to have access to a window station, the desktop, etc., which is where you would see run-time exceptions. I'd take a punt and see if it works (once you've fixed your compilation error). What they're really saying here is "don't call us if this breaks" :)

Accessing LinqBridge from JSON.NET dll

I am using JSON.NET which has the LinqBridge .dll merged in. LinqBridge allows Linq to be accessed from .NET 2. If I try to use Linq, even after importing System.Linq, I receive the following error:
Error 13 Could not find an implementation of the query pattern for source type 'int[]'. 'Where' not found. Are you missing a reference to 'System.Core.dll' or a using directive for 'System.Linq'? C:\Users\chrisl\Desktop\SoftTokens\Windows Desktop Soft-Token\Program.cs 27 25 WindowsSoftToken
If I try to include LinqBridge, then because JSON.NET already includes it, I receive this warning. Additionally, I have included the same component twice, which is inefficient:
Warning 2 The predefined type 'System.Action' is defined in multiple assemblies in the global alias; using definition from 'c:\Users\chrisl\Desktop\SoftTokens\Windows Desktop Soft-Token\libs\Newtonsoft.Json.Net20.dll' WindowsSoftToken
If I browse Newtonsoft.Json.Net20 in the object browser, I see that System.Linq appears empty, even after I have slected Show hidden types and methods.
Is it possible to access Linq from the JSON.NET dll or to suppress the error messages?
The Enumerable static class that provides the LINQ query operators in the LINQBridge assembly is still exposed in the System.Linq namespace.
You still need to have the using directive for System.Linq as directed in your first error message.
Update:
It turns out the LINQBridge assembly that's merged into Newtonsoft.Json.Net20.dll has been "internalized" which I hadn't noticed at first. This means your code can't reference the Enumerable type that the compiler needs to "implement the query pattern." So you do have to reference the LINQBridge assembly yourself, but then you get the warning about duplicate definitions as you mentioned.
You can disable the duplicate class warning by going to the Build tab of your project properties and enter "1685" in the "Suppress warnings:" box.
But what would probably be better would be to build your own version of JSON.net from source without merging in LINQBridge.

Is there a way to run code when an assembly loads?

I'm building an application that will load untrusted assemblies for inspection (i.e. retrieval of the assembly full name). For security reasons, I'm trying to think of a way that these assemblies could be written that would allow them to execute code when loaded. I haven't code up with a method yet, but wanted to throw it out here to see if anyone could.
I'm aware that I could load these assemblies into an untrusted app domain, effectively stopping them from doing almost anything, but I wanted to avoid the complexity if it's un-needed.
Specifically, I will be calling Assembly.Load and <LoadedAssebmly>.FullName. Maybe there's a better way to load the assembly name without using the Assembly class?
Thanks,
Matt
First of all, there's the AssemblyName class. It allows you to find the assembly's name without loading it. Second, you can load assemblies using Assembly.ReflectionOnlyLoad, which uses the reflection-only context -- no code can be executed from such an assembly.
Yes, it is possible: .Net: Running code when assembly is loaded
I suggest, you use a method to inspect the assembly, that doesn't load it, i.e. Mono.Cecil

Assembly unavailable after Web.config change

I'm using a custom framework that uses reflection to do a GetTypeByName(string fullName) on the fully-qualified type name that it gets from the database, to create an instance of said type and add it to the page, resulting in a standard modular kind of thing.
GetTypeByName is a utility function of mine that simply iterates through Thread.GetDomain().GetAssemblies(), then performs an assembly.GetType(fullName) to find the relevant type. Obviously this result gets cached for future reference and speed.
However, I'm experiencing some issues whereby if the web.config gets updated (and, in some scarier instances if the application pool gets recycled) then it will lose all knowledge of certain assemblies, resulting in the inability to render an instance of the module type. Debugging shows that the missing assembly literally does not exist in the current thread assemblies list.
To get around this I added a second check which is a bit dirty but recurses through the /bin/ directory's DLLs and checks that each one exists in the assemblies list. If it doesn't, it loads it using Assembly.Load and fixing the context issue thanks to 'Solving the Assembly Load Context Problem'.
This would work, only it seems that (and I'm aware this shouldn't be possible) some projects still have access to the missing assembly, for example my actual web project rather than the framework itself - and it then complains that duplicate references have been added!
Has anyone ever heard of anything like this, or have any ideas why an assembly would simply drop out of existence on a config change? Short of a solution, what is the most elegant workaround to get all the assemblies in the bin to reload? It needs to be all in one "hit" so that the site visitors don't see any difference other than a small delay, so an app_offline.htm file is out of the question. Programatically renaming a DLL in the bin and then naming it back does work, but requires "modify" permissions for the IIS user account, which is insane.
Thanks for any pointers the community can gather!
Generally, you should avoid relying on what assemblies are currently loaded in an appdomain, as that happens dynamically. Instead, simply call System.Web.Compilation.BuildManager.GetType() instead of Type.GetType() or Assembly.GetType(). This should just do the right thing for you, and not be affected by appdomain cycles.
As you obviously know, there are many situations where the current appdomain is unloaded and reloaded. After each reload, all assemblies are unloaded and the whole application starts running "from scratch".
Assemblies are by default loaded on demand. Usually that is the case when the JIT stumbles across some reference. In consequence, a appdomain reload will clear out the assemblies in the appdomain and they will only appear again later on when the JIT loads them.
As solution I'd rever to using the static Type.GetType() method and supply an assembly qualified name (e.g. a type name with the assembly name included). That's the same thing the framework uses when loading types specified in the config file, and it will make sure that the required assembly is searched and loaded on demand without using any tricks. See the remarks section of the method above (the method name above name is a link).
This will require updates to your database to hold assembly qualified names instead of "only" fully qualified type names. However, it also makes sure that you don't run into name collisions when two assemblies provide different type with the same name, so this is a good idea anyways I think.
I've never heard of this problem before.
I'm not sure if this will work, as I only recently read about it while researching workarounds to ODAC dependencies, but specifying the probing path for assemblies may fix your issue.
see: http://msdn.microsoft.com/en-us/library/823z9h8w(VS.80).aspx
sample:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin;bin2\subbin;bin3"/>
</assemblyBinding>
</runtime>
</configuration>
I have a similar problem, when I update 2-5 files, ether web.config, ether other files, and asp.net needs to recreate the running files, then some times did not find some classes/function that exist on dll files, and my system is not working - throw errors like yours.
My solution to that is that I place the app_offline.htm on the root, make my updates, then rename/remove the app_offline.htm and my system works fine.
I am sure that have something to do with the cached compiled files, but I did not have deaply search whats exactly cause that.
[what is the most elegant workaround to get all the assemblies in the bin to reload]
Now what is the most elegant workaround on this is to call the HttpRuntime.UnloadAppDomain and actually make your application to stop and starts again.
http://msdn.microsoft.com/en-us/library/system.web.httpruntime.unloadappdomain(VS.80).aspx
I do not know if this solve your issue, you need to make tests.
probably on Global.asax make something like that
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError().GetBaseException();
...if ex is your error, and you get more than 2 ...
{
HttpRuntime.UnloadAppDomain();
}
}
I would try to create some basic class from which assembly, which is interesting for you without reflection on Application Start to make sure it is loaded.
E.g.
var temp = new BaseModuleBuilder();
This do not look smart, but it is very straitforward and asp.net should do everything for you. In case when your list is too dynamic, it could be something like
var temp = Activator.CreateInstance(Type.GetType("BaseModuleBuilder, Modules.dll"));
Make sure to always specify DLL when working with dynacmically loaded classes.

Resources