Entity Framework database first on dll strange behavior - c#

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.

Related

SSIS Script Task Reflection reference permissions

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

MEF Different Assembly Version Loading Issue

I have a bit of a conundrum with MEF.
I have an installer and configuration application shell which uses MEF to load individual installer components. This gives an end user the ability to select from whatever components have been placed into the install distributable.
The first install components which were written to use this used version 11 of SQLServer SMO libraries. Installing against either 2008R2 or 2012 works fine. (lets call this component A)
I have another team migrating code to a new component but that code uses version 10 of the SQLServer SMO, RMO, and SSIS (DTS) libraries. (lets call this component B)
When MEF goes to load component B I get a LoaderExceptionFailure for one of the SQLServer DLLs (Microsoft.SqlServer.Replication). It actually gives a FileNotFoundException (listing the DLL). The DLL exists in the component's directory. It is the correct version 10.
The shell application already has version 11 files in it.
Is there a way I can tell the application context what to do? Is there a way I can tell the component to load any specific libraries it needs?
I want to assume that each component can specify something "Associated.Library, Version=1.0.0.0, publickey=abcdef123456789, culture=none".
The reason the CLR (not MEF) cannot find the assembly is because it is neither in the GAC, not in the places were the current AppDomain is setup to probe for assemblies.
One way to deal with this kind of problem is to add the missing assembly to the GAC.
Another approach is to add the folder containing the missing assembly to the probing paths of your application. If you don't mind deploying these assemblies in the application base folder (the one containing your executable) then do so. Otherwise you can add deploy it in a sub folder of your application base folder and add the folder to the privatePath element of your app.config.
You will find more information on the article: How the Runtime Locates Assemblies.

Loading C# DLL to C++/CLI - dependencies directory

I wrote a dll c++/cli library which uses my other c# dll library. C++/cli library works fine when I've got c# dll in the same folder as application which calls it. This library will be finally loaded to many applications and a C# dll must not be copied into directory with application. It has to be in the same folder as c++/cli library, but in that cases I've got System.IO.FileNotFoundException.
My suggestion is to load c# library manually or to change path where f.ex. firefox is looking for dependencies, but I tried with LoadLibrary() and Assembly::LoadFrom() methods to force loading from right directory. Of course I added directory path with dll to system PATH.
I work on VS2010.
You don't change the default directory where an application will look for dlls.
At design time put your dll in some well know location, the one you are going to deploy to. Add a reference to it, make sure it's set to Don't copy ever, otherwise it will end up in the bin folder. You have to do this otherwise it won't compile.
When you deploy, you'll need one package to deploy common dlls, and one for each application. Careful you don't create your own version of dll hell, if appA needs an older or new version of the common dll, compared to AppB
Add an AppDomain.AssemblyResolve event to main (for windows app). At run time the event handler will get fired when you reference a type in your dll, and the assembly has not yet been loaded.
In there you load it from the well known location. That usually in config, or in a relative path.
E.g.
AllMyApps
CommonDLLS
MyFirstApp
So the path you load the required common dll from would be "..\CommonDlls\MyCommondll.dll".
NB you will want to secure the dlls in some way, otherwise a bad guy might be able to inject their version of one in to your app, which would be bad...
You can use this mechanism to get the dll from a remote server or a database blob as well.
The simplest case is only a few lines of code, just look the event up. Took me about 15 minutes to get this going in a similar scenario.
Not on this machine though, otherwise I'd have pasted in the code.

How do I relocate assemblies from a deployment project without breaking application references?

I have recently refactored a lot of my applications existing code and I am now looking at tidying up the deployment side of things.
The existing installer application installs everything in the application folder (with the exclusion of a couple of config files which are located in a sub folder). However, I have multiple applications which all use some common assemblies and my goal is to relocate these particular assemblies to the "Common Files" folder in the program files directory.
NB: I have read a lot about the GAC but I have no experience with it and also read a few horror stories, so trying to get a simple solution for the time being.
I managed to get the assemblies installed into the Common Files folder, however, as a result (typical I.T.) I have broken my app! If I copy the assemblies back into the application folder it works fine so the problem is obviously to do with how my app is referencing the assemblies.
To get the installer to install the assemblies into the Common Files folder I just updated the Folder property of each assembly in the Detected Dependencies list. My thoughts were when I did that the installer would somehow update my application to tell it to look in that folder for them but that doens't appear to be the case.
What exactly am I doing wrong here?
There should be no requirements for assemblies to be in the GAC, unless the developer of an application/library designed it so. You have the choice to write you application so that most (if not all) referenced assemblies load from a specified (Common Files) location.
Here's an example architecture that implements the technologies described in the MSDN articles referenced at the bottom of this response.
Example: In an SOA application you might have a couple of different (Windows) services. Services could be load balanced across multiple servers. Within each server, services can be installed under a 'Services' directory. Services living in the 'Services' directory could share assemblies from a (Common Files) 'lib' directory:
\CompanyName
\Services
\Service1
\Service2
\Service3
\lib
Every actual service would derive from a Base Service class that would make use of an Assembly Utility. Your Assembly Utility could be configured to search for assemblies in a systematic way, allowing you to use shared/common assemblies. The neat thing is that your application could run with local assemblies (in local development) but use shared assemblies when deployed.
In my real world example, I had the luxury of having custom build and deployment scripts. Think of the different scenarios you can have deploying 1 of N services. Do you always update the (Common Files) 'lib' directory? Can a service run with local assemblies different than the 'lib' assemblies? Etc.
I hope this was helpful. If your issue is getting a third-party installer to deploy your application correctly, then disregard and name the installer. Otherwise, the given example/solution should help :o)
Read on the subject at MSDN:
Programming With Application Domains and Assemblies
Resolving Assembly Loads
ResolveEventHandler Delegate
PS: I've had challenges resolving assemblies for Microsoft's Unity framework.
If you wish to "reference" some assemblies from common folder, it is possible at development time. However when deploying every application has to have those individual assemblies installed with them.
If at run time, more than one of your applications are sharing some assemblies then "that common folder" is GAC.

A problem regarding dll inheritance

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.

Categories