0

I have an asp.net application. I want to load some assemblies dynamically.

this is my code on application start

    protected void Application_Start(Object sender, EventArgs e)
        {
            LoadPrivateAssemblies();
        }
    private static void LoadPrivateAssemblies()
        {
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;

            Assembly.Load("MyDLL");
        }
    static Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
        {
            //loads and returns assembly successfully.
        }

this code works fine except when a nested c# code calls a class from my dynamic dll Inside an asp.net page (not code-behind)

sample :

<%if(MyDLL.TestObject.Value){%>white some ting<%}%>

what should I do now ?

I think If I know When a new AppDomain is created, it may solve my problem.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
Farzin Zaker
  • 3,578
  • 3
  • 25
  • 35
  • In what way does it fail? Are you getting errors? And what has this got to do with a new AppDomain being created? – Rob Levine Apr 05 '11 at 11:23
  • all of my cs codes run successfully, but when iis wants to compile my code nested in aspx pages, shows an assembly not referenced error (assembly loaded dynamically in appdomain). – Farzin Zaker Apr 05 '11 at 16:20
  • Removed the [assembly] tag, which is used for assembly-language questions. – Stephen Canon Apr 18 '11 at 18:53

3 Answers3

2

I really think you are barking up the wrong tree here.

Assembly.Load(byte[]) does "persist" the assembly in the app domain - otherwise, what else would it be doing?

To illustrate the fact it does, try this:

Create a solution with one console application, and one class library, named OtherAssembly.

In the class library, OtherAssembly, add a single class:

namespace OtherAssembly
{
    public class Class1
    {
        public string HelloWorld()
        {
            return "Hello World";
        }
    }
}

In the console application, use this as your program:

public class Program
{
    static void Main(string[] args)
    {
        try
        {
            using (var fs = new FileStream("OtherAssembly.dll", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                var buffer = new byte[fs.Length];
                // load my assembly into a byte array from disk
                fs.Read(buffer, 0, (int) fs.Length);

                // load the assembly in the byte array into the current app domain
                AppDomain.CurrentDomain.Load(buffer);
            }

            // get my type from the other assembly that we just loaded
            var class1 = Type.GetType("OtherAssembly.Class1, OtherAssembly");

            // create an instance of the type
            var class1Instance = class1.GetConstructor(Type.EmptyTypes).Invoke(null);

            // find and invoke the HelloWorld method.
            var hellowWorldMethod = class1.GetMethod("HelloWorld");
            Console.WriteLine(hellowWorldMethod.Invoke(class1Instance, null));
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
        finally
        {
            Console.ReadLine();
        }
    }
}

Don't reference OtherAssembly from your main program assembly, instead, compile the solution and manually drop OtherAssembly.dll in the main program's bin folder.

Run the main program. It outputs "Hello World", which it can only have done if the assembly was loaded and retained in memory. You'll note I've been very careful not to give Visual Studio or C# any hint to load this OtherAssembly. No reference was added, the type is not explicitly referenced in C#.

You need to look again at your problem.

[EDIT: in response to you commenting on the fact this is not an ASP.NET application]

OK - I've moved my main program to an ASP.NET web page and tried accessing the assembly both from code behind and the markup - and it works in both situations. I'm sure you are missing something here - it just doesn't make sense that the behaviour of this method - whose job it is to load an assembly into the current app domain - is different in an ASP.NET scenario.

It seems to me there are at least two things to investigate:

  • If it really is a new app domain being created on each call, it would only be because of an unhandled exception in your application. The quickest way to diagnose this is to use SysInternals Process Explorer. Fire it up - add .Net -> Total AppDomains to the list of columns and watch the app domain count for your process (IIS worker process or web dev) as you run your app. If this increases on each call, you've got unhandled exceptions tearing down one AppDomain and forcing another to be created.
  • Are you sure this assembly is really being loaded? When you pass the byte array to Assembly.Load, try also writing it out to disk using FileStream.Write. Open the file in Reflector or some similar tool. Is this the assembly you expected it to be. Is it really being loaded at all, or is it an empty/corrupt array?

I'm not trying to be argumentative, but this really feels like you are looking in the wrong place for the cause of the problem.

Rob Levine
  • 40,328
  • 13
  • 85
  • 111
  • this is a windows application, my problem is in asp.net pages. read my question more carefully. your sample works, but not in asp.net! – Farzin Zaker Apr 05 '11 at 16:19
  • Just to clarify - which overload of Assembly.Load are you saying doesn't work in this asp.net scenario? Your answer below suggests Assembly.Load(byte[]), but your original question above is Assembly.Load(string). Also, in your example, you access MyDLL.TestObject.Value. Is Value a static property on the type TestObject? – Rob Levine Apr 05 '11 at 17:34
  • Ya, My Problem is with Assembly.Load(byte[]), this overload does not persist loaded assembly in appdomain. especially in ASPX pages (not code behind). – Farzin Zaker Apr 05 '11 at 18:10
  • I tried these before. I have only two app domains witch I asked mscoree.tlb to find them for me for iis process on all requests. also I tried saving my assembly on disk and opening it with reflector. every thing was ok. even if I save my byte[] to disk and then load that file using Assembly.LoadFile(path), every thing goes fine. sorry if I am boring you. but this is really stopping me from deadline. – Farzin Zaker Apr 05 '11 at 19:09
  • So - in that case - are you sure that Assembly.Load(byte[]) isn't throwing an exception or otherwise failing? Can you explain why you have two app domains - this really isn't clear in your question. What is the second app domain and who created it? – Rob Levine Apr 05 '11 at 19:20
  • yes, Its loading assembly without problem persisting it in appDomain. I have found my appDomains from IIS Worker process. one for itself and one for my application. so I have only one appdomain in my application context. – Farzin Zaker Apr 06 '11 at 06:02
1

I think If I know When a new AppDomain is created, it may solve my problem

You should use AssemblyLoad event. AssemblyResolved occurs when the resolution of assembly is fails

vvk
  • 833
  • 5
  • 16
  • AssemblyLoad does not work here. I want to load my assemblies dynamically. some of my assemblies are not placed in bin folder. – Farzin Zaker Apr 05 '11 at 09:00
  • @Farzin: What do you want to know when to load an assembly or how to load an assembly? – vvk Apr 05 '11 at 09:40
  • I placed my assemblies in a encrypted file. so I should load my assemblies in appdomain dynamically. I used assembly resolve event to correct this problem. but assembly resolve just works for code witch is placed in code behind of my pages (.cs) not for codes witch placed inside aspx pages. so it fails to load assembly and raises an exception. (assembly not referenced) – Farzin Zaker Apr 05 '11 at 09:46
  • to use your assembly in the aspx file, use the @Assembly directive – vvk Apr 05 '11 at 09:59
-1

I found that the problem is because Assembly.Load(bytes); does not persist the assembly in appdomain. does any body know how to persist loaded assembly using Assembly.Load(bytes); in appdomain ?

finally I decided to switch to LoadFile method instead of load.

EDIT

Finally I switched from Assemly.Load(byte[]) to Assembly.LoadFile(string).

But it did not correct the problem itself. I have added <%@Assembly name="MyDLL"%> To All of my ASPX files that has markup C# code emebeded.

And This Solved my Problem.

Thanks to your answers. I have voted up answers that helped me, but I can not accept your solutions because no one is not complete enough.

Farzin Zaker
  • 3,578
  • 3
  • 25
  • 35
  • 1
    This can't be correct. When you load an assembly into an AppDomain, it is always persisted. In fact, it is impossible to *unload* it without tearing down the AppDomain – Rob Levine Apr 05 '11 at 11:23
  • You can try it yourself. it is not corrent about Assembly.load(byte[]) but in other methods it is correct. that is because there is no file for iis to take in use. try it and then undo your vote down. – Farzin Zaker Apr 05 '11 at 14:36
  • I've posted an example. I'm afraid you are wrong - I wouldn't have written the comment unless I'd tried it. My code sample above demonstrates that Assembly.load(byte[]) does persist the assembly - otherwise what else would it be doing. You need to look again at your problem. I suspect wk may be on the right track. – Rob Levine Apr 05 '11 at 15:07
  • this is a windows application, my problem is in asp.net pages. read my question more carefully. – Farzin Zaker Apr 05 '11 at 16:18
  • In fact, loading from byte arrays loads the assembly in a different "context" of the AppDomain. There are 3 different contexts that I cannot name from memory. Dig deep into MSDN, if you need to know the details, e.g. there is one context for "reflection-only" assemblies. Assemblies in the "Load(byte[])" context are not resolved automatically. To solve this problem you need to implement the AppDomain.AssemblyResolve event. See http://stackoverflow.com/questions/11122052/need-to-hookup-assemblyresolve-event-when-disallowapplicationbaseprobing-true/19702548#19702548 for hints on how to do that. – No answer Jan 22 '14 at 10:34