1

I have a .net application that references the Microsoft.SqlServer.Smo assembly. The assembly is not distributed with the application. Instead the sql sdk is installed in the system and the dll is registered in the GAC so that the application can load. There is no problem with this except that on some target machines I have the v12 of the SDK, while on others I have the v13 of the SDK (that usually comes installed with SSMS). I would like the application to load the latest version of whatever is available on the system, so v13 or, if not available, v12. Is it possible to achieve this in code or through the application config?

gigi
  • 796
  • 9
  • 21
  • Is your development machine/build machine configured with the Microsoft.SqlServer.Smo assembly in the GAC just like the target machines? – sevzas Mar 21 '17 at 14:46
  • yes my dev machine has the v11, v12 and v13 in the GAC – gigi Mar 21 '17 at 14:55
  • 1
    Seems like the way to go is to set the SpecificVersion property of the reference to Smo assembly to false in your .csproj file. See if this answer helps http://stackoverflow.com/questions/1063459/net-reference-specificversion-true-or-false – sevzas Mar 21 '17 at 15:14
  • if I do that on my dev machine for some reason I get an exception: {"Could not load file or assembly 'Microsoft.SqlServer.Smo, Version=13.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91' or one of its dependencies. The system cannot find the file specified.":"Microsoft.SqlServer.Smo, Version=13.100.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"} – gigi Mar 21 '17 at 15:46
  • Does that error message appear at build time or runtime? Can you edit your question and post the lines from your .csproj that reference the Smo dll? Did you make sure that there are no copies of Smo dll on your machine (except in the GAC)? – sevzas Mar 21 '17 at 15:55
  • this appens at runtime. so, the 13.0 is installed in the GAC while 13.100 (it's un update) is not. the project references the 13.100 that is installed in C:\Program Files (x86)\Microsoft SQL Server\130\DTS\Tasks\Microsoft.SqlServer.Smo.dll (not in the GAC). I was under the impression that this should automatically fall back to the GAC version at runtime. – gigi Mar 21 '17 at 16:06
  • Change the project to reference the GAC. Clean, rebuild, make sure that there is *not* a copy of Smo.dll in the final output directory. – sevzas Mar 21 '17 at 16:22
  • SpecifcVersion is acutally working you are correct. It is loading the latest version available in the GAC. anyway I get the error as described above and this is why: https://connect.microsoft.com/SQLServer/feedback/details/3106613/net-exception-could-not-load-file-or-assembly-microsoft-sqlserver-sqlclrprovider-13-100-0-0-after-installing-sql-server-2016 So your solution could work for a standard dll, but not these dirty microsoft dll – gigi Mar 21 '17 at 17:04

1 Answers1

0

The short answer to the question is to set SpecificVersion to false as correctly suggested by @sevzas.

Anyway, if on the system is installed SSMS 2016 update 13.0.16000.28, the 13.100.0.0 of the dll will be registered in the GAC and with the above change, this is the version that it will be loaded. Unfortunately this version is not meant to be used by 3rd party developers but only by Microsoft products, so trying to load it will generate an exception (see here). Someone could wonder at this point why they register it in the GAC if they don't want people to use it.

Anyway, I found a way to load the v13.0 (or previous, or future 14) with the below code by using the assembly resolve event.

    static int Main(string[] args)
    {
        //we set an event handler at the begging of our program
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        //your stuff
    }

    private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        //if the dll is a sqlserver dll, we do our trick
        if(args.Name.StartsWith("Microsoft.SqlServer"))
            return LoadSqlAssembly(args.Name);

        return null;
    }

    private static readonly int[] SqlVersions = new int[] {14, 13, 12, 11};

    private static bool _reEntry = false;

    private static Assembly LoadSqlAssembly(string name)
    {
        if (_reEntry)
            return null;

        name = name.Split(',')[0];

        foreach (var version in SqlVersions)
        {
            try
            {
                _reEntry = true;
                var ret = Assembly.Load($"{name}, Version={version}.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL");
                //Logger.InfoFormat("Loaded {0} version {1}", name, version);
                return ret;
            }
            catch (Exception)
            {
                //ignore exception
            }
            finally
            {
                _reEntry = false;
            }
        }

        return null;
    }

```

Community
  • 1
  • 1
gigi
  • 796
  • 9
  • 21