none
"Specific Version" & "Copy Local" settings are ignored RRS feed

  • Question

  • I have a c# project that references SSIS assemblies that I'd like to build as version independent (i.e. I want the program to execute on machines that have SSIS 2008 or SSIS 2012). I've gone through and set the Specific version flag on all of the references to false. I've also set Copy Local to false. In the .csproj file, it looks like this

    <Reference Include="Microsoft.SQLServer.DTSPipelineWrap">
       <SpecificVersion>False</SpecificVersion>
       <Private>False</Private>
    </Reference>
    <Reference Include="Microsoft.SQLServer.DTSRuntimeWrap">
       <SpecificVersion>False</SpecificVersion>
       <Private>False</Private>
    </Reference>
    <Reference Include="Microsoft.SQLServer.ManagedDTS">
       <SpecificVersion>False</SpecificVersion>
       <Private>False</Private>
    </Reference>
    <Reference Include="Microsoft.SQLServer.PipelineHost">
       <SpecificVersion>False</SpecificVersion>
       <Private>False</Private>
    </Reference>
    <Reference Include="Microsoft.SQLServer.SQLTask">
       <SpecificVersion>False</SpecificVersion>
       <Private>False</Private>
    </Reference>
    <Reference Include="TransferSqlServerObjectsTask">
       <SpecificVersion>False</SpecificVersion>
       <Private>False</Private>
    </Reference>

    When I complile the project it builds successfully. I'm building on a machine that has SSIS 2012 and it runs successfully there. However, when I run the program on a machine that only has SSIS 2008, it fails with errors like

    "Could not load file or assembly 'Microsoft.SqlServer.ManagedDTS, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91' or one of its dependencies. The system cannot find the file specified. Please see the diagnostic report for more details."

    I've scoured my other projects and ensured that these assemblies aren't referenced anywhere else. Any ideas why this isn't working?

    As a side note, I notice that DTSRuntimeWrap and SQLTask dll are both copied over to my build's output directory. So the CopyLocal flag is being ignored for them for some reason. Again, I looked through all my projects to make sure they're not referenced somewhere else. I also check to make sure all of the SQL assemblies mentioned above are in the GAC.

    It seems like both of these flags are being ignored. Any help would be greatly appreciated!

    Thanks,

    Bill

    Thursday, March 28, 2013 5:48 PM

Answers

  • There is a very big misunderstanding about what these reference properties do.  It comes up time again in the forums. 

    Specific Version doesn't say that your code can run against any version at runtime.  You're referencing a strongly named assembly so the assembly version you compile against is the version that will be used.  The only way to work around that is to add a binding policy that says your app can work with differing versions. 

    The purpose of SV is to determine what version you will use when you compile.  When you add a reference to a strongly named assembly and if SV is true then the version that was referenced when you added it is the version that will be built against.  If SV is false then any version could be used.d  This is strictly for determining, at compile time, what version you'll reference.  An example: suppose you add a reference to a third-party component D v1.0.  When you compile your code is tied to v1 at runtime.  Later you upgrade your machine to D v2.0.  The GAC still has a copy of both v1 and v2.  When you compile your project next time if SV is set then you will compile against v1 (even though v2 is available).  If SV is not set then you will build against the newer v2.  Irrelevant whatever version you compiled against is the version required at runtime.  By default SV is false because we normally want to compile against the latest version of an assembly.  The few times you'd set SV to true is if there is a compatibility issue with a newer version so you want to ensure that you always use a specific version.  In summary, no impact on runtime requirements.  If you want to support either version then you'll have to add a binding element to your config file.

    Copy Local is true for non-strongly named assemblies and false for SN (IIRC). What this determines is if the assembly will be copied to the output directory at build time. For non-SN the runtime will only look for the assembly in the app path (and a few other places). So in most cases you need the non-SN assembly to be in the output directory otherwise it cannot be loaded. For SN the runtime will always look in the GAC first. Since most SN assemblies are in the GAC then there is no need to have the assembly in the output (it wouldn't use it anyway).  However if you have an assembly that isn't in the GAC then you can set CL to true to ensure it ends up in the output directory so it can be found at runtime.

    Michael Taylor - 3/29/2013
    http://msmvps.com/blogs/p3net

    • Marked as answer by Foshay Monday, April 8, 2013 4:26 PM
    Friday, March 29, 2013 2:49 PM
    Moderator

All replies

  • There is a very big misunderstanding about what these reference properties do.  It comes up time again in the forums. 

    Specific Version doesn't say that your code can run against any version at runtime.  You're referencing a strongly named assembly so the assembly version you compile against is the version that will be used.  The only way to work around that is to add a binding policy that says your app can work with differing versions. 

    The purpose of SV is to determine what version you will use when you compile.  When you add a reference to a strongly named assembly and if SV is true then the version that was referenced when you added it is the version that will be built against.  If SV is false then any version could be used.d  This is strictly for determining, at compile time, what version you'll reference.  An example: suppose you add a reference to a third-party component D v1.0.  When you compile your code is tied to v1 at runtime.  Later you upgrade your machine to D v2.0.  The GAC still has a copy of both v1 and v2.  When you compile your project next time if SV is set then you will compile against v1 (even though v2 is available).  If SV is not set then you will build against the newer v2.  Irrelevant whatever version you compiled against is the version required at runtime.  By default SV is false because we normally want to compile against the latest version of an assembly.  The few times you'd set SV to true is if there is a compatibility issue with a newer version so you want to ensure that you always use a specific version.  In summary, no impact on runtime requirements.  If you want to support either version then you'll have to add a binding element to your config file.

    Copy Local is true for non-strongly named assemblies and false for SN (IIRC). What this determines is if the assembly will be copied to the output directory at build time. For non-SN the runtime will only look for the assembly in the app path (and a few other places). So in most cases you need the non-SN assembly to be in the output directory otherwise it cannot be loaded. For SN the runtime will always look in the GAC first. Since most SN assemblies are in the GAC then there is no need to have the assembly in the output (it wouldn't use it anyway).  However if you have an assembly that isn't in the GAC then you can set CL to true to ensure it ends up in the output directory so it can be found at runtime.

    Michael Taylor - 3/29/2013
    http://msmvps.com/blogs/p3net

    • Marked as answer by Foshay Monday, April 8, 2013 4:26 PM
    Friday, March 29, 2013 2:49 PM
    Moderator
  • Thanks Michael for the detailed response! This makes a lot of sense. At least it clears up the use of the SpecificVersion and CopyLocal properties. However, I'm still a little confused regarding the binding element. So, in order for an application to support both SSIS 2008 & SSIS 2012 assemblies, I have to set up the config file according to the environment? Is there no way to just set this up at compile time? So that my application will run on a system that only has SSIS 2008 or only SSIS 2012 without having to manually modify the config prior to running it? If I add a binding element to my app config, won't that force the application to try and use whatever I specify as the "newVersion" of the assembly, regardless of whether it's on the system or not?

    And, if I compile against the latest version of the SSIS assemblies, is it even possible to set up a bindingRedirect to point to the old version of the assemblies so that my application will still run on SSIS 2008 machines?

    Thanks again for your help!

    Bill

    • Edited by Foshay Friday, March 29, 2013 9:47 PM
    Friday, March 29, 2013 8:26 PM
  • If you need to support both 2008 and 2012 in the same assembly then you'll have to target the 2008 version.  On my machine I don't see that the SSIS assemblies are in the GAC so you shouldn't run into any issues provided either the 2008 or 2012 version is in the path that the loader will look. 

    In my experience, working with reporting and SSIS in the past, I'm not sure that you'll be able to support both versions in the same assembly though.  You should review what has changed between versions and ensure that there hasn't been a fundamental change such that you have no choice but to upgrade.  In that case you could still accomplish the goal of having a shared assembly of the core data but you'd have to have separately named assemblies for each version.  I haven't worked with SSIS in a while so I cannot say for certain.  You might do better to post that question in the SSIS forums.

    Here's a starter link to the upgrade process: http://www.sqlservercentral.com/blogs/dknight/2012/05/16/upgrading-packages-to-ssis-2012/  It might provide more insight as to whether an SSIS 2008 package will even work without change in SSIS 2012.

    Saturday, March 30, 2013 2:59 AM
    Moderator
  • This works! I compiled specifically against the 2008 version. At this point, the application runs as expected on machines that have SSIS 2008 installed. If I want to run it on a machine with only 2012 installed, I added a binding element to my config file as suggested.

    <runtime>
       <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <dependentAssembly>
             <assemblyIdentity name="Microsoft.SqlServer.DTSPipelineWrap" publicKeyToken="89845dcd8080cc91" culture="neutral"/>
             <bindingRedirect oldVersion="10.0.0.0" newVersion="11.0.0.0" />
          <dependentAssembly>
       </assemblyBInding>
    </runtime>

    We still need to do more testing (and review what's changed between SSIS versions as you suggested), but at this point, this looks like it allows our application to run successfully with 2012.

    However, this binding config element will break the application on the 2008 machine as it looks to run with the newer assembly. Is it possible to make this binding redirection conditional (i.e. if SSIS 2012 assemblies exist, run against them, otherwise stick with the 2008 assemblies)? We would prefer clients not have to change the config file if at all possible.

    Monday, April 1, 2013 5:07 PM
  • If you want to use only one or the other then a static binding redirect won't work.  Have you confirmed that a machine with SQL2012 installed does not include the assembly version that you need?  MS often ships older assemblies so that apps can continue working.

    If there is no older assembly then you'll have to revert to programmatic redirection via AppDomain.AssemblyResolve.  This event is raised when the framework cannot find an assembly.  In your case you'll probably want to compile against SQL 2008.  If the assembly cannot be found then the event is raised.  In that case you can try to load the later version, if available.  If it is then you'll return back the assembly and the loader will continue on.  If it doesn't exist then the load will fail as you would expect.

    Michael Taylor - 4/1/2013
    http://msmvps.com/blogs/p3net

    Monday, April 1, 2013 6:04 PM
    Moderator
  • This is a bit old thread, but I have stumbled upon a similar CopyLocal issue which ruined my deployemnts for long time. ANd here is a workaround:

    http://blogs.msdn.com/b/jjameson/archive/2009/11/18/the-copy-local-bug-in-visual-studio.aspx

    Essentially - if an assembly is in GAC on your local machine and you want to force it to CopyLocal, you have to toggle the CopyLocal property twice to make it work.

    And also it seems that the CopyLocal=true does not affect the references above it. For example, I have a project Entities which references some.dll in GAC. If I force CopyLocal=true for some.dll as described above, everything seems fine - some.dll gets copied to the bin folder of Entities.

    But then if I reference the Entities project in some other project Services and expect to get some.dll copied to the Services\bin, it won't happen - Services project ignores CopyLocal=true of the Entities project and still grabs the some.dll from the GAC. This is true even for Visual Studio 2012.

    Microsoft, when will this bug be fixed?


    • Edited by Martin Sall Tuesday, August 27, 2013 12:30 PM
    Tuesday, August 27, 2013 12:29 PM