I have built software that is capable of exporting DTSX package automatically. This package among other objects has also a ScriptTask (C#). All are compiled and run just fine.
Now the new requirement is to call a class in that ScriptTask, which exists inside an external DLL we have built, so other applications can consume the same code. So we implemented a loading mechanism of the DLL, using reflection.
Assembly clientLib = Assembly.LoadFrom("C:\\......\\mylibrary.dll");
Type licenseCheckType = clientLib.GetType("myclass");
object instance = Activator.CreateInstance(licenseCheckType);
The code is running just fine, but there are several permission issues during deployment.
Running the package from DataTools, the package fails because cannot find the file. If I executed the DataTools as administrator is working. Still, I don't get it, as a user I should be able to browse the file.
Running the package from SQLAgent(this is important), the package fails. If I set the SQL Agent service user with "Local System" or "Administrator" user the is working.
I am looking for a way to find how I can make it work with a simple user by adding permissions to that path if necessary, but still, keep failing. I tried to set a simple domain user as SQLAgent Service account and give Full Control as a start to the directory without success.
Error: Exception has been thrown by the target of an invocation.
Implementing the loading mechanism from GAC, is working, but I would like to explore the other way as the DLL in my case, this DLL is referencing a LiteDb file in the system directory, and it would be nice to have them all together.
Assembly clientLib = Assembly.Loadm("mylibrary");
Type licenseCheckType = clientLib.GetType("myclass");
object instance = Activator.CreateInstance(licenseCheckType);
As a summary:
Using the GAC
UI will reference the local DLL because exist in bin folder
SSIS will reference the GAC DLL because of loading from GAC, which references a db file from local folder
Using the Local (problematic)
UI will reference the local DLL because exist in bin folder
SSIS will reference the local DLL because of loading from local folder, which references a db file from local folder
Related
I have a remote SQL Server database, wrapped with Entity Framework inside a dll.
When I reference that dll from my main application, I get a runtime exception stating "missing connection string in app.settings file" or, if I manually add the connection string, "no Entity Framework engine found".
If instead, I install EF from NuGet ALSO in my main application, the referenced dll works perfectly.
Now, the reason why I created and referenced the dll was to detach my business layer from the persistence layer, but if I need anyway a reference to EF for my application to work, I'm loosing the advantage of the dll wrapping.
What should I do?
EDIT:
I can't add a dependency from my main app to EF simply because the user of that dll will be a client outside .NET, such as an Excel or Matlab instace
You will have the same issue with all your dependencies. You must distribute multiple DLLs and make sure they get loaded. See
See Resolving Assembly Loads
The AssemblyLoad event allows you to take over the assembly loading process for your AppDomain, and you can find the dependent assemblies on disk outside of the normal search locations (which are dependent on the host .exe location), or download them, or unpack them from assembly resources in your main .dll.
See also How to load an Assembly in a SSIS script task that isn’t in the GAC
The dll NEVER has it's own config file that it accesses to read settings such as connection strings. Although your code to read the config information may be in an assembly, the config file is owned by the host process, so an entry for connection strings MUST be present in the Host processes config file.
In your scenario, it sounds like on the remote sever it will be SQL server that acts as the host process. On your local machine, the host process (your .exe or website, IIS Wp3 ) will act as the host process. Your local process WILL require the connectionstring setting in web.config / app/config.
In VS, when we add a reference to an assembly project, all of the dependent .dll's get compiled and placed in the top level applications bin folder. If you simply reference a .dll file from your application, these dependent .dll files are not placed in the bin folder. This is what you are experiencing here with EF.
It is possible to encapsulate EF functionality inside a .dll file, but the .dll files which make up EF itself, MUST be available to the host process. You don't need to add a NuGet reference to your main application. By referencing the project that you .dll is built from in your main application, the EF engine will be compiled to the bin folder of your main application.
Once you have built and deployed your main application with a project reference to your assembly, all the required files will be in the folder from which you execute your application. You can then make changes to the .dll source and just update the .dll binaries in the application folder, providing the changes do not break contracts etc eg. method signatures.
I have:
SSIS/.dtsx package with a script task in SSIS that I added references to. It works fine locally (assuming the .dlls are in the GAC).
When depolying on the server it failes (assuming the .dlls are NOT references in the GAC) and I can not add them.
The SSIS package is stored in Integration Services on the SQL server in the Stored Packages - MSDB
The job runs on a schedule
I do not have access or the ability to add .dlls or add items to the GAC on the server.
What I need to do is find a way to include the .dll in the script task inside the SSIS package so that the References point to those .dlls instead of any in the GAC.
I searched quite a bit and could not find a way to do this. Is it even possible? If so what/how do I do it?
Locally it does work, in server it cannot find a required reference. what referenced dll is missing? you know it?
Once you know what dll you need, get a copy of it and, even if you cannot install it on the server GAC, you can deploy it in the same folder you deploy your executable.
At runtime, your executable will first try to load the dll from the same folder, if not found, it will try searching in GAC.
Say I have a Visual Studio Project that references a libary XYZ.dll. I am not able to distribute that dll but I know that many people have a license for it.
What can I do to connect my project to XYZ.dll on the target computer? To be more precise, I want to do the following things:
Reference XYZ.dll in a project in Visual Studio.
Distribute a compiled version of the solution/project without XYZ.dll
Let the customer, who installs my program, link the program to his copy of XYZ.dll so that the program can use it.
(This may be an easy question, but I was not able to find the answer, maybe due to wrong search terms).
If the XYZ.dll is installed with a third-party product, you may check whether it is registered in GAC.
If so, then you - in your VS project - reference the XYZ.dll pointing to it in GAC and then setting the copy local to false, so that it will not be copied to your program's bin directory and used from there.
It becomes more problematic in case the dll is not in GAC - in such case you would need to ask user for the assebly's location (or read it from registry if you know what product to search for) and then resolve this assembly dynamiccaly using that path with the use of AssemblyResolve event (https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve%28v=vs.110%29.aspx)
There is also a way in which you create a "proxy" class in your project that loads the third-party assembly dynamiccaly from the path on the customer's computer, and then create a set of proxy methods that would call loaded third -party assembly using reflection.
How can i have 'Create Assembly' reference the build output path.
I have an SQL CLR that references a web service.
I have an entry in the PostDeployment script
CREATE ASSEMBLY TestWebServiceClientXML from N'\\TestServer2012\clrdeploy\Test\TestWebServiceClient.XmlSerializers.dll' WITH permission_set = EXTERNAL_ACCESS
Currently this works fine. I build the TestWebServiceClient separately manually, that builds the dll and XMLSerializer dll to a share that SQL can see. When i then publish the database project with the CLR in that references this, the PostDeployment scripts uses the XMLSerializers dll I've just built from the share.
How can I get this scenario to work with a build server? All the build outputs, including the XMLSerializers dll will go to a single build output path on the build server and when the post deployment script runs it will create an assembly from an old version on the share '\TestServer2012\clrdeploy\Test\' as this is hard coded, not the version just built.
Eventually I want this to run within Release Management. Anyone done this before?
Using SQLCMD variables, have the build server pass the build folder's path to SqlPackage.exe as a SQLCMD variable. Then modify your post deployment script to look something like this:
CREATE ASSEMBLY TestWebServiceClientXML from N'$(BuildFolder)\clrdeploy\Test\TestWebServiceClient.XmlSerializers.dll' WITH permission_set = EXTERNAL_ACCESS
I have created a dll that will be used by multiple applications, and have created an installer package that installs it to the program files, as well as adds it to the Global Assembly Cache.
The dll itself uses log4net, and requires a xml file for the logging definitions.
Therefore when the installer is run, the following files get copied to the install directory within program files:
The main dll that I developed
- The Log4Net.dll
- the Log4Net.xml file
I am now experiencing a problem. I have created a test console application for experimentation. I have added my dll as a reference, and set the 'local copy' flag to false.
When I compile the test console exe however, I noticed that it has copied the log4net.dll and log4net.xml files to the bin directory. And when running the test console, it appears that it will only work if the log4net.dll is in the same directory as the exe. This is dispite the fact that the test console application does not use log4net, only the dll that was added as a reference does.
Is there some way to have it so that the log4net.dll & xml files used will be the ones that were installed to the program files, rather than any application needed to copy over local copies? The applications that will be using my dll will not be using log4net, only the dll that they are referencing uses it.
Many thanks
Don't install into the Global Assembly Cache! Even if your library dll is used by multiple applications each should have it's own local copy. Otherwise you get into a whole world of pain for saving a few KB of disk space.
Always copy the required dlls locally. If you are really sure that the application won't need it you can simply delete the unnessesary dlls later or don't include them in the installer. But if your application will call ANY reference there it will crash at runtime. So best option is to leave them there (after all they WERE referenced for a reason).
No, it's not possible (at least not without much efford) to have .Net load dlls from arbitrary locations on the disk. And it should be this way (look up DLL-hell if you want to know why).
I suspect your problem is the configuration. You must use fully qualified names if you want it to work from the GAC. As per the documentation at http://logging.apache.org/log4net/release/faq.html:
"When loading an assembly from the GAC the fully qualified assembly name, including the version, culture and public key must be specified. This is in the standard syntax supported by System.Type.GetType. See the next FAQ on how to get the version and public key for an assembly."
I managed to resolve this by adding Log4net.dll to the GAC as well. It will now run without needing a local copy the dll.
It does however require a local copy of the XML file, to correctly log.