Is it possible to import a namespace from an assembly that is not in the bin folder?
I'm using ASP.NET MVC 2 with MEF to pull controllers out of the assembly.I was able to get everything working, however, strongly typed views can't recognize the assemblies' objects unless the assembly is in the bin folder.
Strongly typed view means knowing the object type at compile time. In order to know the object at compile time the assembly containing the class needs to be referenced. Referencing assemblies in an ASP.NET application is done by putting them in the bin folder.
If you use reflection to load assemblies from some other non-standard location, types will be known only at runtime and you cannot use them as models for strongly typed views.
If you want to load assemblies from somewhere else than the bin folder at runtime, you may do so by calling the appropriate method through the AppDomain.
Related
First of all, please forgive me for not knowing the proper terminology, I'm sure there's a very common technical name for this which I could simply Google for help - but I can't find help if I don't know the term to begin with.
I'm building a modular system in Delphi 7. There are a few applications and a bunch of DLL's. All the applications share these DLL's, as well as some of the DLL's use other DLL's. The DLL's are currently saved in the same location as the application. I would instead like to put all these DLL's in a sub-folder (elsewhere from the EXE's) but of course Delphi won't know how to find them.
Is there a way I can direct my Delphi Apps to look in a certain directory for DLL's? It can't be using Contstants, because there will be an option to specify where the DLL's are stored.
These DLL's are just a plain collection of StdCall functions in each, nothing special.
EDIT:
To explain the reason why I want to keep the DLL's in their own folder: This system I'm building considers these DLL's as add-ons. By default, the system might not even have any add-ons. On the other hand, it will also allow various vendors to build other DLL's and include them as add-ons. Then each application requiring these Add-ons will be directed to the folder where to find them.
The application its self will have its own DLL's which will be in the same directory as the applications. But the Vendors' DLL's I would like to keep separate.
As mentioned in the answers below, my best bet would be to implement the DLL Import method, because A) I can specify a path for each DLL it's importing, B) I can better control the use of each DLL (Does it need to be loaded or not?) and C) Each DLL can technically be in separate folders by themselves (Vendors might want to build their own folder structure). This system is still very pre-mature but I plan to make further flexibility with it.
If you are dynamically loading the DLLs in your code, you can store them whereever you want since you have to pass the full path to LoadLibrary/Ex() anyway. If you are statically linking to the DLLs instead, then you can use SetDllDirectory() to designate an additional path to include in the OS's DLL search path.
You can do this with PATH but I recommend you don't. It's a brutal and inflexible approach. And of course you need to change the system wide PATH for it to have any effect at executable load time.
You can load your DLLs explicitly with LoadLibrary and GetProcAddress. That's no fun if there are a lot of imports but it can be a good option otherwise. And remember that if you go down this route, every single DLL must switch to explicit linking.
There is something called DLL Redirection but MS don't recommend you use that. They recommend that you use side-by-side components. Having said that, the Visual Studio team moved away from side-by-side components with the MSVC runtime in VS2010 because of the pain that side-by-side had caused in previous release.
So, in spite of all the options, I really believe that the best solution is to put all the DLLs in the same directory as the executable. If you can get over the folder looking untidy then it will make life much simpler. It is a trivial no effort solution to the problem.
Update
The update to your question provides the extra information that these DLLs are optional add-ons. In this case you simply have no alternative but to use explicit linking with LoadLibrary and GetProcAddress.
I would highly recommend that you leave the DLL's in the same folder as the applications.
If you really want to go down the road of putting the DLL's in a separate folder then you need to know whether you can load the DLL's with the LoadLibrary API which allows the specification of the path too. However, if the DLL's are statically loaded then it is Windows that performs the search. The Windows search first looks in the application folder then searches the Windows PATH. Also, as Delphi 7 only creates 32 bit applications this can get messy under Windows 64 bit.
On Windows, there is a "DLL search order". One of those search paths is The directory from which the application loaded, which is why it works to have them in the same folder as the EXE.
If you are statically linked to the DLLs, they must be loaded when the EXE is loaded into memory. This is before your first line of code is executed. So, you're relying on the DLLs being in one of the search paths. In this case, you're stuck with setting the path, and you must set it prior to the program loading.
If you are dynamically linking to the DLLs, then you can use LoadLibrary/LoadLibraryEx to load the DLLs at run time, in your code. Using those functions, you must specify the path to the DLL, so the DLLs could be anywhere. In this case, I feel that it's valid to put the DLLs in a separate folder to keep things tidy. As long as you don't put the DLLs into a shared location like the Windows System32 folder, you'll avoid a lot of headaches.
A temporarly solution is:
You can set your DLL path in your application's Shortcut (in "Start in" box).
When you add a new view to an ASP.NET MVC project you can strongly type the view by selecting which model class it should work with in the dialog box. The problem is in that drop down menu it includes all the classes and interfaces from all referenced projects and dlls. So, I see all the Ninject classes, all the interfaces for my services, etc in that menu. Is there any way to specify which namespaces to include or a list of namespaces to exclude from that drop down menu?
I realize that this is not a big deal and you can just start typing the name of the class in the box and it will pull up the right one. I just thought that if there was a place for this it would be nice. If there isn't, no big deal but I thought I would ask. :)
If you really really want this and you are cool with some hacking, try this :)
c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\Microsoft.VisualStudio.Web.Mvc.3.01.dll
decompile it - you will find MvcAddViewDialog class, and the GetTypes method in it. Put some filtering in it, recompile, replace, enjoy... but personally, I probably wouldnt :)
Is there a way within FSI that I can reference and use registered COM components?
In a normal .fs compiled program I can simply reference the component in question and then open the relevant generated namespace(s). In a .fsx file, however, I can't seem to replicate this behaviour. I have tried using #r to reference the .dll directly, and I have tried using #I to point to the directory followed #r both with the library's "friendly" name and the file name, but nothing seems to work.
Are you only able to reference .NET assemblies from a .fsx? I don't really want to have to write/gen a wrapper assembly. I am hoping there might be a way to force FSI to take whatever steps the normal executable takes in order to provide the interop layer.
When you add a reference to a COM component in Visual Studio, it invokes a tool to generate a wrapper (standard .NET assembly) and then references the wrapper.
If you want to reference COM from fsx, you'll need to generate the wrapper yourself (or find the one generated by Visual Studio?) The tool that generates the wrapper that is called TlbImp.exe (see Type Library Importer on MSDN).
In my ASP.NET MVC application, I have the following setup:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin;extras"/>
I have referenced assemblies located in the extras folder in the views and they have worked perfectly (using <%# Import Namespace="myNameSpace" %>).
My questions
What happens when that line is called?
Where is the assembly loaded?
Why is it that I can't overwrite the assembly located in the extras folder that contains myNameSpace with a newer version? (I get an error saying that the assembly is "open" in another program)
Is there a way to overwrite the assembly with a newer version without having the application restart?
1) Import doesn't actually do anything at runtime. It's a compile-time convenience that just lets you refer to types using their unqualified names such as Environment instead of System.Environment.
2) The assembly is loaded using normal assembly probing rules. The CLR checks various locations before these private probing paths so it's important to keep that in mind. If you reference a strong-named assembly and expect to find that assembly in a private probing path, an assembly with the same strong name (name, version, public key, etc) in the GAC would be preferred. This can sometimes lead to unexpected behavior and is usually caused by hard coding an assembly version in your AssemblyInfo.cs and forgetting to update it.
3) Once loaded, an assembly cannot be unloaded without unloading the AppDomain. But ASP.NET uses "shadow copying" which means assemblies are copied to a temporary path before being loaded. This should leave the original assembly unlocked and able to be overwritten. Off the top of my head, I am not sure why you'd be getting the error about locked assemblies. In a normal Windows application, this would be totally normal and expected. But ASP.NET is designed so that you can overwrite content, code, assemblies, etc while the application is running, which leads to #4.
4) In practice, no. Because an assembly cannot be unloaded, there is no way to upgrade an assembly without the web application being restarted. Technically speaking, you can load multiple versions of an assembly but this would not give you the desired results. Any compile-time references would still reference the old assembly and you'd get all kinds of invalid cast exceptions if you tried to use the new assembly. But as I said in #3, with ASP.NET upgrading assemblies is supposed to be as simple as replacing the files and should happen automatically. You shouldn't have to restart IIS or worker processes manually.
The following links may be of interest.
How the Runtime Locates Assemblies
Best Practices for Loading Assemblies
Shadow Copying Assemblies
Unloading Assemblies - Suzanne Cook
UPDATE
After reading a bit more on shadow copying, I think the reason you might be seeing the issue of locked assemblies in the extras folder is that ASP.NET probably only specifies the "bin" folder for shadow copying.
I think this is the same as a using statement in C#, it basically means that the namespace's classes are now available to be used in yoru page.
The assembly will be loaded into memory probably by the aspnetwp.exe process
If the assembly is currently being used you will get this error message
A restart is the safest way I know of doing this, you could use dependency injection or late binding to achieve the same result. I would jsut wonder why you would want to switch an assembly while the application is running?
I like the new Add View dialog that was released with RC1. If you specify a view data class, it generates a scaffolded view for you. It seems to work fine with classes defined in projects within my solution. Some of my domain classes inherit from a base class that is defined in an external assembly (and referenced in my web project). I get an error when I specify one of these domain classes. Visual Studio is reporting a TypeLoadException on the external base class.
I did a little debugging with ProcMon and found that it was looking for the base class DLL in, among other places, a temporary folder. That folder did contain the DLL containing my derived domain model class. I did not see the base class's DLL. So for fun, I copied it there and voila, I could add the view.
Is this a bug?
Yes this is a bug. Please file it on http://codeplex.com/aspnet
you actually forgot to add the Web.Config file on the View Folder.
See this small article : http://www.graytechnology.com/Blog/post/ASPNET-MVC-strongly-typed-view-error-Could-not-load-type.aspx
It got me scratching my head quite a bit as well in my current project! :)