Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Options for using System.Data.SQLite in a 32bit and 64bit C# world

I understand WHY the System.Data.SQLite.dll is provided in 32 bit and 64 bit builds. So lets not dwell on that and move on. :)

Since it is done this way it seems to make pure C# development a tad more difficult with 3 choices to make.

  1. Is to support only 32-bit and force there managed assembly to compile x86 and deal with that in running in 32 or 64 bit, and there by lose advantages of when you are on a 64 bit environment.

  2. Is to force 64 bit and only support 64 bit and losing the ability to run on 32 bit but gaining all the advantages of 64 bit.

  3. Is to create two versions of their assembly one that compiles x86 and uses 32 bit SQLite and another that compiles x64 and uses 64bit SQLite. It prevents using "ANY" as a compile option and being able to easily deploy a single build to either type. Its not so horrible to manage from a development point of view as we will need two projects. Only having the C# code officially in one, and the other will just use "links" to the code in the other. This is for compiling purposes only. Still leaves us with having to manage two outputs to for deployments.

With all that said I am only looking for confirmation that the above are the only correct choices.

If however there are other choices that I am overlooking please let me know. Specifically if there is way to get a single C# DLL that can compile to ANY so it can take advantage of 32 or 64 bit depending on where its run and still use System.Data.SQLite.dll.

like image 963
Rodney S. Foley Avatar asked Aug 31 '11 22:08

Rodney S. Foley


4 Answers

This is an elaboration of the answer from Springy76. Do this:

public class AssemblyResolver
{
    public static void HandleUnresovledAssemblies()
    {
        AppDomain currentDomain = AppDomain.CurrentDomain;
        currentDomain.AssemblyResolve += currentDomain_AssemblyResolve;
    }

    private static Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        if (args.Name == "System.Data.SQLite")
        {
            var path = Path.Combine(pathToWhereYourNativeFolderLives, "Native");

            if (IntPtr.Size == 8) // or for .NET4 use Environment.Is64BitProcess
            {
                path = Path.Combine(path, "64");
            }
            else
            {
                path = Path.Combine(path, "32");
            }

            path = Path.Combine(path, "System.Data.SQLite.DLL");

            Assembly assembly = Assembly.LoadFrom(path);
            return assembly;
        }

        return null;
    }
}

Make sure the paths generated point to the correct locations for either your 32 bit or 64 bit SQLite Dlls. Personally I've had good results with those in this NuGet package: http://www.nuget.org/packages/SQLitex64

(You only need to use the NuGet package to get hold of the compiled SQLite Dlls. Once you've got them, remove the reference to SQLite in your project created by NuGet and the NuGet package itself. Indeed, leaving the reference in place can interfere with this solution as SQLite will never be recognised as an unresolved assembly.)

Call 'HandleUnresolvedAssemblies()' as early as possible, preferably during any Bootstrapping.

like image 102
Holf Avatar answered Oct 21 '22 01:10

Holf


There are 2 common solutions for keeping your main application at AnyCPU:

  • Install both the x86 and the x64 assemblies into the GAC: They can (should!) have identical assembly names and the GAC will automatically decide whether to use x86 or x64 version.

  • Hook into AppDomain.AssemblyResolve and serve the right assemblies from subdirectories using Assembly.LoadFrom

like image 34
springy76 Avatar answered Oct 21 '22 02:10

springy76


The similar problem exists with Oracle's ODP.NET vis-a-vis native 32/64-bit OCI DLLs.

We have solved it by crating both 'x86' and 'x64' platform for our project and then manually editing our project file to use the conditional references:

<Choose>
<When Condition="'$(Platform)' == 'x64'">
  <ItemGroup>
    <Reference Include="Oracle.DataAccess, processorArchitecture=x64">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\ThirdParty\ODP.NET\x64\Oracle.DataAccess.dll</HintPath>
    </Reference>
    <Content Include="..\ThirdParty\ODP.NET\x64\oci.dll">
      <Link>oci.dll</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="..\ThirdParty\ODP.NET\x64\orannzsbb11.dll">
      <Link>orannzsbb11.dll</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="..\ThirdParty\ODP.NET\x64\oraociei11.dll">
      <Link>oraociei11.dll</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="..\ThirdParty\ODP.NET\x64\OraOps11w.dll">
      <Link>OraOps11w.dll</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
</When>
<When Condition="'$(Platform)' == 'x86'">
  <ItemGroup>
    <Reference Include="Oracle.DataAccess, processorArchitecture=x86">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\ThirdParty\ODP.NET\x86\Oracle.DataAccess.dll</HintPath>
    </Reference>
    <Content Include="..\ThirdParty\ODP.NET\x86\oci.dll">
      <Link>oci.dll</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="..\ThirdParty\ODP.NET\x86\orannzsbb11.dll">
      <Link>orannzsbb11.dll</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="..\ThirdParty\ODP.NET\x86\oraociei11.dll">
      <Link>oraociei11.dll</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="..\ThirdParty\ODP.NET\x86\OraOps11w.dll">
      <Link>OraOps11w.dll</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
</When>
</Choose>

This enabled us to avoid using 2 different projects. I'm sure you can do something similar for SQLite.

like image 40
Branko Dimitrijevic Avatar answered Oct 21 '22 01:10

Branko Dimitrijevic


Its not so horrible to manage from a development point of view as we will need two projects.

That's not true - you can use two build configurations within the same project. At deployment time, you'll need to build for both x86 and x64, but the code base and project can be the same.

I currently do this in a larger production project, both due to SQLite, but also other native/interop libraries that include x64 and x86 variants.

like image 30
Reed Copsey Avatar answered Oct 21 '22 01:10

Reed Copsey