1
votes

I am trying to access an SQL Server Compact 3.5 database using Entity Framework 6 using EntityFramework.SqlServerCompact.Legacy 6.1.3.

My application is a Visual Studio extension and there is no way that I can add anything to the application config file.

If I make a simple test application, I can connect to the database without any problems. If I try to access the database from a class library it fails with the error message

Schema specified is not valid. Errors: ProjectTemplate.ssdl(2,2) : error 0152: No Entity Framework provider found for the ADO.NET provider with invariant name 'System.Data.SqlServerCe.3.5'. Make sure the provider is registered in the 'entityFramework' section of the application config file. See http://go.microsoft.com/fwlink/?LinkId=260882 for more information.

I have found some interesting links, for example here and here but I can't get it to work, either because the example apply to EF5 or to SQL Server (or I have wool over my eyes).

How do I do it?

1
There is no way you can do this, you must require the SQL Server Compact 3.5 SP2 runtime to be installed. Your best approach is to test for this when your extension is launched, and the point you users to a download location for the MSI(s). Remember that both the x86 and x64 MSI must be installed on a x64 system. If you use 4.0, the install experience is much better (single MSI)ErikEJ
That is not my immediate problem (but I realize that it is a problem). My problem is I don't know how to initialize the Entity Framework context without having any entries in app.config.Phil Jollans
@ErikEJ thanks for the reply, but I am struggling to get this to work. The link you provided contains multiple solutions. The simplest looks like the one marked "LATE ANSWER". I can instantiate the DbProviderFactory and provide the connection string for the database connection, without the EF metadata. I can't figure out how to create the DbContext with both a connection and the metadata specifying the EDMX file. I have also tried the accepted answer (from JoshRivers) but I can't get that to work either. I have not yet tried the IDbDependencyResolver method.Phil Jollans

1 Answers

0
votes

I am currently testing with a console application and a class library. The console application knows nothing about Entity Framework and simply calls a method in the class library.

static void Main(string[] args)
{
  EFTest t = new EFTest();
  t.Test();
}

This is the code in the class library.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Core.EntityClient;

namespace EF6_SQLCompact_CleanTest_000
{
  public class EFTest
  {
    public void Test()
    {
      try
      {
        // Add an event handler for DbConfiguration.Loaded, which adds our dependency 
        // resolver class to the chain of resolvers.
        System.Data.Entity.DbConfiguration.Loaded += (_, a) => {
          a.AddDependencyResolver(new MyDependencyResolver(), true);
        };

        // Define the provider connection string, specifying the SQL Server Compact 3.5 filename.
        String FileName = @"f:\DotNetTestProjects\2015\CS\SQLCompact35DllTest\SQLCompact35DllTest\ProjectTemplate.sdf";
        String ConnectionString = String.Format("Data Source={0}", FileName);

        // Create the entity framework connection string, specifying the model,
        // the provider and the provider connection string.
        var builder = new EntityConnectionStringBuilder();
        builder.Metadata = @"res://*/ProjectTemplate.csdl|res://*/ProjectTemplate.ssdl|res://*/ProjectTemplate.msl";
        builder.Provider = "System.Data.SqlServerCe.3.5";
        builder.ProviderConnectionString = ConnectionString;

        // Create the entity framework kontext
        Entities Context = new Entities(builder.ToString());

        // Do a trivial query as a test.
        int i = Context.Languages.Count();
        MessageBox.Show(i.ToString());
      }
      catch (Exception e)
      {
        MessageBox.Show(e.Message);
      }
    }
  }

  // Define an additional constructor for the Entities class, so that we can 
  // pass the connection string into the base class DbContext.
  public partial class Entities : DbContext
  {
    public Entities(String ConnectionString)
      : base(ConnectionString)
    {
    }
  }

  class MyDependencyResolver : System.Data.Entity.Infrastructure.DependencyResolution.IDbDependencyResolver
  {
    public object GetService(Type type, object key)
    {
      // Output the service attempting to be resolved along with it's key 
      System.Diagnostics.Debug.WriteLine(string.Format("MyDependencyResolver.GetService({0}, {1})", type.Name, key == null ? "" : key.ToString()));

      if ((type == typeof(System.Data.Common.DbProviderFactory))
           && (key != null)
           && ((string)(key) == "System.Data.SqlServerCe.3.5"))
      {
        return System.Data.SqlServerCe.SqlCeProviderFactory.Instance ;
      }
      else if ((type == typeof(System.Data.Entity.Core.Common.DbProviderServices))
                && (key != null)
                && ((string)(key) == "System.Data.SqlServerCe.3.5"))
      {
        return System.Data.Entity.SqlServerCompact.Legacy.SqlCeProviderServices.Instance ;
      }
      else if ((type == typeof(System.Data.Entity.Infrastructure.IProviderInvariantName))
                && (key is System.Data.SqlServerCe.SqlCeProviderFactory))
      {
        return new MyProviderInvariantName();
      }

      return null;
    }

    public IEnumerable<object> GetServices(Type type, object key)
    {
      return new object[] { GetService(type, key) }.ToList().Where(o => o != null);
    }
  }

  // Implement IProviderInvariantName so that we can return an object when
  // requested in GetService()
  class MyProviderInvariantName : IProviderInvariantName
  {
    public string Name
    {
      get { return "System.Data.SqlServerCe.3.5"; }
    }
  }
}

The dependency resolver is based on based on the answer from Ryan Griffith in the link mentioned by EricEJ.

This appears to work in the context of my test program, but I have not yet tried it in the context of a Visual Studio Extension.

So far as I can tell, this technique will only work if an event handler is added to DbConfiguration.Loaded before the DbConfiguration is loaded for the first time in the AppDomain (which I might not be able to guarantee). I am also worried that it may have side effects for Visual Studio or for other Visual Studio extensions.