I am trying to dynamically map one or more assemblies, that are themselves dynamically loaded into an ASP.NET website. I have my web.config setup to allow probing into a subdirectory called "paks" (which is also setup as an appSetting so I can build my path later to get to the DLLs) and this works 100% fine to load the main assemblies, but FluentNHibernate is having issues:
My .Mapping looks like this:
.Mappings(m => {
foreach (String assembly in assemblies) {
String path = String.Format("{0}{1}\\{2}.dll", HttpRuntime.AppDomainAppPath, ConfigurationManager.AppSettings["Packages"], assembly);
AssemblyName y = AssemblyName.GetAssemblyName(path);
Assembly asm = Assembly.Load(y);
m.FluentMappings.AddFromAssembly(asm);
}
})
when I run it however I get an exception from :
"Could not compile the mapping document: (XmlDocument)"
but if I copy a second set of the DLLs into the \bin directory, magically things begin to work. I'm assuming because now NHibernate can locate the DLLs, whereas previous it couldn't. I don't have any issues loading them dynamically into .NET using Assembly.Load() so why is NHibernate having issues after they've been loaded?
Anyone have a clue as to how I can remedy this?
Sorry about this, turns out these was a slight misunderstanding on my part about ASP.NET probing and assembly loading.
I thought that once an assembly was loaded it would be able to be located by objects that referenced it. But it seems that is not the case. To make matters worse, I thought probing would recursively traverse down into sub-directories (which it apparently does not) and was required for the initial loading I was doing (which is also not the case). My assembly resided in %root%/pak/pdf but my probe was only set for %root%/pak which is why, even though it was loaded, it could not be found by the other assemblies that referenced it.
Related
Getting error
A reference to "file path\file.sln"could not be added. please make
sure that the file is accessible and that its is a valid assembly or
COM component
You mean adding a reference inside a project?
If is this, you can´t add a reference to a whole .sln, you will need to choose, for example, a valid .dll of the service you are trying to reference.
A reference to "file path\file.sln" could not be added.
That's a solution file.
[...] and that its is a valid assembly or COM component
A solution file is not an assembly or a COM component, hence the error. You need to add a reference to an actual assembly. If it's a project in your current solution, add a Project Reference. If it's not in your solution (and for whatever reason can't be added, though I highly recommend adding it if at all possible) then you'll need to add a reference to the compiled .dll of the referenced project.
You can't add references to solution files, project files, anything like that. Those are just XML metadata about projects. You need the compiled output, the assembly.
Question:
If I have multiple projects in one solution is it still considered a single assembly?
Background Information:
I'm aware the 'MyApplication/Properties/AssemblyInfo.cs' file exists. Further, I confirmed that when I:
Add a project to the solution.
Appropriately reference the newly added project.
Lastly, Build the solution.
The 'MyApplication/Properties/AssemblyInfo.cs' file has not changed. This leaves me to believe, and please correct me if I'm wrong that I'll have met the demand.
Thank you
No.
Each project is compiled into one assembly in your case. The assemblyinfo.cs file (for each project) should not change at all when you compile anything. Also, that file's name is not important at all; it's the global attributes inside it that cause various properties of the assembly being created to be set. That file's name and location are simply a convention.
I have reference a custom assembly in an ssis script task. The script task apparently works correctly but the assembly (first tested with a console program) doesn't work anymore.
The assembly is supposed to transform a text file to a csv file for further import. Once loaded in the script task the function referenced does create an empty file. Logging did not bring any addtionnal info.
Currently the assembly is strongly named, signed, in the GAC as well as in the DTS\SDK folder of MSSQL server.
Any idea ?
Actually the error was in my own assembly. So to summarize the correct and easy way to include and use an assembly file in a ssis script task is :
Compile the assembly with the correct framework version (in my case 3.5)
Sign the assembly
Put the assembly in the GAC
Reference the assembly within the script task and add an "imports" directive
Actually putting the assembly in the DTS\bin folder did not produce any difference.
This is why I don't usually like using custom assemblies in SSIS. In this case, I would write a jig (exe) to call the assembly and change SSIS to call my exe. In the jig (exe) I would wrap the call to the assembly in a try/catch block. I would trace the incoming parameters and log any errors to a text file or to the system Event Log. Usually, this approach gives me enough insight into the real problem (bad param, permission problem, weird data, etc) and a good way to recreate the problem and verify a solution.
I have a project that uses log4net and works fine on the developer machines. When we build, a step in our build scripts calls the AssemblyInfo task to set version numbers and dates, etc. But the AssemblyInfo file also loses the line:
[assembly: XmlConfigurator(Watch = true)]
Is there any way to have the AssemblyInfo task not overwrite that line, or conversely, to have the AssemblyInfo task re-insert that line into the file (along with the appropriate using statement?
You can actually put the log4net [assembly: ...] line in any file that will be compiled into the assembly. A lot of times, we just put it in the .cs file that has the "main" method, and it works just fine.
Well, thinking a little differently, you don't have to have only one assembly info file.
This is how we do it for a solution, each project has 3 assembly info files:
AssemblyInfo.cs - Contains Guid, AssemblyTitle, AssemblyDescription, and any assembly specific attributes.
ProductAssemblyInfo.cs - Contains AssemblyProduct, AssemblyVersion and AssemblyFileVersion. This way all assemblies in a product/solution share the same version.
CompanyAssemblyInfo.cs - Contains AssemblyCompany, AssemblyCopyright, and any attributes we want all our assemblies to share.
Items 2 and 3 are referenced using "Add as link", this way they are shared from the single source code. Now, you may not want to adopt the same approach, but you could certainly use a similar structure and place your XmlConfigurator in a separate assembly info file from your assembly version details.
I'm racking my brain trying to come up with an elegant solution to a DLL load problem. I have an application that statically links to other lib files which load DLLs. I'm not loading the DLLs directly. I'd like to have some DLLs in another folder other than the folder that the executable is in. Something like %working_folder%\dlls - I'd rather not have dozens (yes ... dozens) of DLLs in my %working_folder%.
I'm trying to develop something that is part of the main app that will adjust the search path # startup. The problem I'm running into is that this new custom DLL path isn't in the system search path. When I start the app it crashes (STATUS_DLL_NOT_FOUND) because the necessary DLLs are not in the appropriate places. What I'd like to do is to check # startup if this new custom DLL folder is in the process environment variable search path and if not add it. Problem is, the application attempts to load all these DLLs before the app executes one line of code.
How do I fix this? I've considered writing a help app that starts first, adjusts the environment variables appropriately and the launches the main app via CreateProcess. This will work I'm sure of it but it makes things difficult on the developers. When they debug the main app they're not going to launch a helper app first - not that they could even do that.
I've tried the registry app path feature with no success. Same chicken and egg problem as before.
What can I do here?
I found Matthew's answer worked for me.
In visual studio 2012 goto your project properties and in
Configuration Properties->Linker->Input->Delay Loaded Dlls
add each dll file that you want to not load until needed.
Although it no longer needs to run before main, this is my code to set the new search path
class RunBeforeMain
{
public:
RunBeforeMain()
{
const TCHAR* dllPathEnvName= name of env variable to directory containing dlls
const TCHAR* pathEnvName= TEXT("Path");
TCHAR newSearchPath[4096];
::GetEnvironmentVariable(dllPathEnvName, newSearchPath, MAX_PATH);
//append bin
_tcscat_s(newSearchPath, MAX_PATH, TEXT("bin;"));
size_t length = _tcslen(newSearchPath);
//append existing Path
::GetEnvironmentVariable(pathEnvName, newSearchPath + length, 4096-length);
::SetEnvironmentVariable(pathEnvName, newSearchPath);
}
};
static RunBeforeMain runBeforeMain; //constructor code will run before main.
[Edit - after re-reading the question I see that the problem you're having is that the DLLs are getting loaded before main starts]
I'm guessing that those libraries are written in C++ and are loading the DLLs from the constructor of some objects in global scope. This is problematic. Allow me to quote Yossi Kreinin:
Do it first thing in main(). If you use C++, you should do it first thing before main(), because people can use FP in constructors of global variables. This can be achieved by figuring out the compiler-specific translation unit initialization order, compiling your own C/C++ start-up library, overriding the entry point of a compiled start-up library using stuff like LD_PRELOAD, overwriting it in a statically linked program right there in the binary image, having a coding convention forcing to call FloatingPointSingleton::instance() before using FP, or shooting the people who like to do things before main(). It’s a trade-off.
[Original answer below]
See this page for the search algorithm used for loading DLLs. You can use SetDllDirectory() to add a directory to the DLL search path.
You also should be able to add a directory to the PATH environment variable using GetEnvironmentVariable() and SetEnvironmentVariable().
Another option is to change the current working directory to the folder containing the DLLs with SetCurrentDirectory(). Just make sure to change the working directory back after loading the DLLs if you ever load any files using relative filenames.
My recommendation is to use delayload linking for the DLLs and call SetDllDirectory() early enough so it can find them when the methods/functions are invoked.