MEF Different Assembly Version Loading Issue - c#

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.

Related

Third-Party DLL I am not allowed to distribute

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.

Resolve 3rd party dll path at runtime

I'm developing an application that depends on some 3rd party dll's (it's actually a plugin to the 3rd party application, and I receive COM objects implementing certain interfaces declared in those dll's). My project contains references to those dll's locally exist on the dev machine, but I want to avoid wraping them inside my installer (for size and legal considerations).
Instead, my installer requires that the dll's are already existing on the target machine, and their path is saved to a text file next to my executable assembly.
How can I use this file to link to the dll's on run time?
Or is it better to add an environment variable during installation and use it instead (how can it be done)?
Using project references, I do not know of a way to have dlls in an arbitrary location. Typically I've handled this issue using an installer, like InstallAware, prompted the user for the 3rd party location, and copied the dlls into my projects folder at the same relative location I chose in my solution file.
If this is improbable for you, you can always load the dlls and create instances of the classes yourself by hand using Assembly.LoadFrom and Activator.CreateInstance.
I hope this helps.

ODP.net works locally but not on other computer

I created a C# project on my computer that uses ODP.net, I imported the reference of Oracle.DataAccess. On my PC, I try to do a connection to the Database and it works normally, however, if I copy the .exe file of my application in another computer, it does not work and I receive the following error:
System.IO.FileNotFoundException: Could not load file or assembly
'Oracle.DataAccess, Version=4.112.3.0, Culture=neutral,
PublicKeyToken=89b483f429c47342' or one of its dependencies. The
system cannot find the file specified. File name: 'Oracle.DataAccess,
Version=4.112.3.0, Culture=neutral, PublicKeyToken=89b483f429c47342'
Why doesn't C# encapsulate all the files needed in the .exe? What can I do to make this program work regardless of the executing computer?
As of request by Vito, here is my comment as an answer:
I suggest to not use the "classic" ODP.NET which has dependencies on additional installed ODP components on the system, but instead use the purely managed version of ODP.NET.
For the managed ODP.NET you have a single assembly (i.e. a DLL) that you can ship with your application (e.g. in the "bin" folder if it is an ASP.NET application) and you're done.
Just to make it complete, the connection string in my cases looks something like:
<add
name="ora"
connectionString="Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=MyServer)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XE))); User Id=MyUser; Password=MyPassword"
providerName="system.data.oracleclient" />
The managed version was really a huge improvement in terms of ease-of-use in my projects.
you must have installed version of the Oracle Database on each machine where you intend to install your application.
Another option is to go to Oracle and download just drivers.
Download from Oracle, then you need to include them with your project, reference this dll.
Please see the answer to a similar question I had. Mainly this problem is about oracle.dataaccess.dll, it's build platform (32 or 64) and presence of some of it's dependencies (like OraOps11w.dll). These things should be checked to see if are present and configured correctly. It would get big in deployment! Of-course if you are calling the library at machine level (not app level) from different applications, you should check if it's registered in GAC too.
Edit: In it's simplest form:
1 - You need to have these dlls in your app directory: OraOps11w.dll, oci.dll, orannzsbb11.dll and oraociei11.dll.
2 - You have to add a reference to Oracle.DataAccess.dll.
Where can one get these?
1 - From the installation directory of you Oracle Client (not Server), if you already had installed the oracle client (including ODP.NET).
2 - If you have installed ODT.NET.
3 - By getting ODP.NET (preferably the zip archive, not the install package).
There are TONS of possible causes to your problem here. I would first start by copying the entire folder than contains the .EXE file, and not just the .EXE file itself. There are things in this folder that your .EXE needs in order to run. Also, I would check the dependencies on the computer that is not working with the program Dependency Walker. Things can go wrong if you develop on a 32-bit and try to run on a 64-bit. Things can go wrong if you develop on Windows and try running on MAC. There is a lot that can change from one computer to the next, and your code must be ready for that. Dependency walker can tell you if you aren't connecting to certain dependancies (mostly .DLL's) correctly. If you go into the release folder that contains your EXE, if there exists any .DLL files in that same directory, these .DLL's are likely needed on any computer that will try and run this program.

How does classic asp access .NET dlls that are referenced by a COM-Visible class?

I have a COM-visible dll "COMInterface.dll" which is deployed into an existing application's folder. I have created a test harness that has verified the functionality of this dll.
However, when I try to use classic asp to call one of this dll's methods I get a message like the following:
MyCompany.MyProject.COMInterface (0x80070002)
Could not load file or assembly 'MyCompany.Framework, Version=2.6.4202.14897, >Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
My "COMInterface.dll" references a number of other dlls such as my company's standard framework, logging classes etc. "Framework" is one of these such dlls.
Where will the asp page be looking for these dlls? I am guessing that my harness worked because it was being run from the program folder whereas the asp page is within an IIS web site.
Do I need to use GAC registration here? I'm hoping not as there would be quite a few dlls I would need to deploy there and I am hoping to extend the application without having to impact the core installation on users' machines.
Thanks in advance of your questions and suggestions.
[EDIT]
I should point out that the object can be created fine. It's the location of the referenced dlls that are causing the problem.
When you use Regasm to register the dll you need to use the /Codebase switch to ensure the dll can be found.
As a general answer, you can use Fusion Log Viewer to determine what paths have been searched by the assembly binder.
See http://msdn.microsoft.com/en-us/library/e74a18c4.aspx for information on this tool.
Fusion Log Viewer will list all the paths probed by the binder. If you put your assembly into any of these paths it will be found. Whether any of these paths will be suitable for your scenario I'm not so sure.

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.

Categories