EF & Mono under Linux

Topics: EF Runtime
Mar 6 at 11:45 AM
Hi,

I want to modify some of my applications to be cross platform and I tried to see how Entity Framework runs under Linux.

Using Monodevelop I start with this code:
class MainClass
    {
        public static void Main (string[] args)
        {
            using (var db = new TraceabilityContext()) 
            { 
                string connString =db.Database.Connection.ConnectionString;
                SqlConnection conn = new SqlConnection(connString);
                conn.Open();
                Console.WriteLine("Connection opened"); 
                conn.Close();

                var ok = db.Database.Exists ();
                Console.WriteLine("EF - OK"); 
                Console.ReadKey(); 
            } 
        }
    }

    public partial class Client
    {
        [Key]
        public short IDClient { get; set; }
        public string ClientName { get; set; }
    }


    public class TraceabilityContext : DbContext 
    { 
        static TraceabilityContext()
        {
            Database.SetInitializer<TraceabilityContext>(null);
        }


        public TraceabilityContext()
            : base("Data Source=192.168.75.104;Initial Catalog=MyDb;User ID=sa;Password=mypass;")
        {
        }

        public DbSet<Client> Clients { get; set; } 
    } 
Everything works perfect under Windows. Unfortunately it is not the same in Linux.
Even under Linux, this part ends with success:
                string connString =db.Database.Connection.ConnectionString;
                SqlConnection conn = new SqlConnection(connString);
                conn.Open();
                Console.WriteLine("OK"); 
                conn.Close();
So the connection string is good.

But when I'm trying to run:
var ok = db.Database.Exists ();
it goes in an exception:
"System.Data.Entity.Core.ProviderIncompatibleException: An error occurred accessing the database. This usually means that the connection to the database failed. Check that the connection string is correct and that the appropriate DbContext constructor is being used to specify it or find it in the application's config file. ...
System.Exception: The provider did not return a ProviderManifestToken string. ..."

Under Ubuntu, Debian, OpenSUSE or Linux Mint is the same.
The database server is SQLServer 2012.

There is someone who successfully manage this?

Thanks in advance !
Mar 6 at 8:41 PM
Do you have the ADO.NET Provider configured in your config file? I was able to make EF work on Mono a while ago (note I used a prerelease version of EF6 and the bugs I hit should now be fixed) - take a look at my blog post http://blog.3d-logic.com/2013/04/14/entity-framework-6-on-mono/. Also there is a blog post here http://www.bgsoftfactory.net/run-asp-net-mvc-4-with-mysql-on-linux/ which expands on the idea and shows the whole scenario.
Mar 10 at 9:41 AM
Thank you for your answer !

I tried to configure the ADO.NET Provider in app.config, without success.
What is strange for me is the message where is said "connection to the database failed"
If I write:
                Console.WriteLine("Status:{0}", ctx.Database.Connection.State);
                //Write on Console: "Status:Closed"
                ctx.Database.Connection.Open();
                Console.WriteLine("Status:{0}", ctx.Database.Connection.State);
                //Write on Console: "Status:Open"
so the connection is working. In fact the same connection "db.Database.Connection.ConnectionString" used to receive data using a DataReader it's working perfectly.

If I'm trying to add System.Data.SqlClient.SqlClientFactory to DbProviderFactories I receive an exception "Column 'InvariantName' is constrained to be unique. Value 'System.Data.SqlClient' is already present.". Indeed getting all DbProviderFactories present "DbProviderFactories.GetFactoryClasses()" the SqlClientFactory is there. I tried even to specify to be defaultConnectionFactory. Nothing helps.

I searched on internet for an example of using Entity Framework with mono in a Linux environment and having SQL Server like data provider. I found just few where MySQL is the provider. Seems EF is not usually used cross-platform.
Mar 10 at 6:43 PM
Ah, I missed that you were trying to use SqlServer even though your app was using Linux/Mono. SqlClient is always registered so if you try register it in app config you will get a duplicate which causes the exception.

Can you post your config file?
Mar 11 at 9:05 AM
I tried several configuration. This is one of the last:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="TraceabilityContext" connectionString="Data Source=192.168.75.104;Initial Catalog=MyDb;User ID=sa;Password=mypass"
      providerName="System.Data.SqlClient" />
  </connectionStrings>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
</configuration>
Meanwhile I used SQL Profiler to see what is happen after the command:
ctx.Database.Connection.Open ();
And it ask to the server:
exec sp_executesql N'select serverproperty(''EngineEdition'')'
After that, the State of ctx.Database.Connection is Open.

If I'm looking with Debug to see the DbProviderFactory of the connection the answer is System.Data.SqlClient.SqlClientFactory...

I built an app using DataSets instead EF and everything works.

Under EF, every request except ctx.Database.Connection.Open ();
goes in "An error occurred accessing the database. This usually means that the connection to the database failed. Check that the connection string is correct and that the appropriate DbContext constructor is being used to specify it or find it in the application's config file ... System.Exception: The provider did not return a ProviderManifestToken string... System.Exception: Read failure"

As I said, if I read with a DataReader using the same ConnectionString (db.Database.Connection.ConnectionString), it works.

I ran out of ideas...
Mar 13 at 12:01 AM
Can you post more details about the exception you are getting - especially a stack trace for the System.Exception: Read failure and all details for inner exceptions (if any)?

I found a couple of threads which may or may not be relevant to your situation but in both cases the exception being thrown read "Read Failure":

http://mono.1490590.n4.nabble.com/WCF-Fail-with-System-Net-Sockets-SocketException-Connection-reset-by-peer-td4650173.html

http://stackoverflow.com/questions/14470887/mono-wcf-client-cannot-talk-to-net-wcf-server-when-transport-security-is-enable

If the connection is being closed by the Sql Server you may try looking at logs and/or eventvwr on the SqlServer machine - there might be some details that could point you in the right direction.
Mar 13 at 9:40 AM
Hi moozzyk and thank you for your time.

The exception is:
System.Data.Entity.Core.ProviderIncompatibleException: An error occurred accessing the database. This usually means that the connection to the database failed. Check that the connection string is correct and that the appropriate DbContext constructor is being used to specify it or find it in the application's config file. See http://go.microsoft.com/fwlink/?LinkId=386386 for information on DbContext and connections. See the inner exception for details of the failure. ---> System.Exception: The provider did not return a ProviderManifestToken string. ---> System.Exception: Read failure ---> System.Exception: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
at System.Net.Sockets.Socket.Receive (System.Byte[] buffer, Int32 offset, Int32 size, SocketFlags flags) [0x0006b] in /build/buildd/mono-3-3.2.4/mcs/class/System/System.Net.Sockets/Socket.cs:1561
at System.Net.Sockets.NetworkStream.Read (System.Byte[] buffer, Int32 offset, Int32 size) [0x00067] in /build/buildd/mono-3-3.2.4/mcs/class/System/System.Net.Sockets/NetworkStream.cs:378
--- End of inner exception stack trace ---
at System.Net.Sockets.NetworkStream.Read (System.Byte[] buffer, Int32 offset, Int32 size) [0x00078] in /build/buildd/mono-3-3.2.4/mcs/class/System/System.Net.Sockets/NetworkStream.cs:380
at Mono.Data.Tds.Protocol.TdsComm.Read (System.Byte[] buffer, Int32 offset, Int32 count) [0x00000] in /build/buildd/mono-3-3.2.4/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/TdsComm.cs:611
--- End of inner exception stack trace ---
at System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken (System.Data.Common.DbConnection connection) [0x00000] in <filename unknown>:0
at System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked (System.Data.Entity.Core.Common.DbProviderServices providerServices, System.Data.Common.DbConnection connection) [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked (System.Data.Entity.Core.Common.DbProviderServices providerServices, System.Data.Common.DbConnection connection) [0x00000] in <filename unknown>:0
at System.Data.Entity.Infrastructure.DefaultManifestTokenResolver+<>c__DisplayClass1.<ResolveManifestToken>b__0 (System.Tuple3 k) [0x00000] in <filename unknown>:0
at System.Collections.Concurrent.ConcurrentDictionary
2+<GetOrAdd>c__AnonStorey45[System.Tuple3[System.Type,System.String,System.String],System.String].<>m__3C () [0x00000] in /build/buildd/mono-3-3.2.4/mcs/class/corlib/System.Collections.Concurrent/ConcurrentDictionary.cs:180
at at (wrapper delegate-invoke) System.Func
1<System.Collections.Generic.KeyValuePair2<System.Tuple3<System.Type, string, string>, string>>:invoke_TResult__this__ ()
at System.Collections.Concurrent.SplitOrderedList2[System.Tuple3[System.Type,System.String,System.String],System.Collections.Generic.KeyValuePair2[System.Tuple3[System.Type,System.String,System.String],System.String]].ListInsert (System.Collections.Concurrent.Node newNode, System.Collections.Concurrent.Node startPoint, System.Collections.Concurrent.Node& current, System.Func1 dataCreator) [0x00065] in /build/buildd/mono-3-3.2.4/mcs/class/corlib/System.Collections.Concurrent/SplitOrderedList.cs:401
at System.Collections.Concurrent.SplitOrderedList
2[System.Tuple3[System.Type,System.String,System.String],System.Collections.Generic.KeyValuePair2[System.Tuple3[System.Type,System.String,System.String],System.String]].InsertInternal (UInt32 key, System.Tuple3 subKey, KeyValuePair2 data, System.Func1 dataCreator, System.Collections.Concurrent.Node& current) [0x00032] in /build/buildd/mono-3-3.2.4/mcs/class/corlib/System.Collections.Concurrent/SplitOrderedList.cs:158
at System.Collections.Concurrent.SplitOrderedList2[System.Tuple3[System.Type,System.String,System.String],System.Collections.Generic.KeyValuePair2[System.Tuple3[System.Type,System.String,System.String],System.String]].InsertOrGet (UInt32 key, System.Tuple3 subKey, KeyValuePair2 data, System.Func1 dataCreator) [0x00000] in /build/buildd/mono-3-3.2.4/mcs/class/corlib/System.Collections.Concurrent/SplitOrderedList.cs:144
at System.Collections.Concurrent.ConcurrentDictionary
2[System.Tuple3[System.Type,System.String,System.String],System.String].GetOrAdd (System.Tuple3 key, System.Func2 valueFactory) [0x00020] in /build/buildd/mono-3-3.2.4/mcs/class/corlib/System.Collections.Concurrent/ConcurrentDictionary.cs:180
at System.Data.Entity.Infrastructure.DefaultManifestTokenResolver.ResolveManifestToken (System.Data.Common.DbConnection connection) [0x00000] in <filename unknown>:0
at System.Data.Entity.Utilities.DbConnectionExtensions.GetProviderInfo (System.Data.Common.DbConnection connection, System.Data.Entity.Core.Common.DbProviderManifest& providerManifest) [0x00000] in <filename unknown>:0
at System.Data.Entity.DbModelBuilder.Build (System.Data.Common.DbConnection providerConnection) [0x00000] in <filename unknown>:0
at System.Data.Entity.Internal.LazyInternalContext.CreateModel (System.Data.Entity.Internal.LazyInternalContext internalContext) [0x00000] in <filename unknown>:0
at System.Data.Entity.Internal.RetryLazy
2[System.Data.Entity.Internal.LazyInternalContext,System.Data.Entity.Infrastructure.DbCompiledModel].GetValue (System.Data.Entity.Internal.LazyInternalContext input) [0x00000] in <filename unknown>:0

There are no evens about this issue under SQL Server.

The ConnectionTimeout is 15 and cannot be changed. If I try:
        public TraceabilityContext()
            : base("Name=TraceabilityContext")
        {
            ((IObjectContextAdapter)this).ObjectContext.CommandTimeout = 300;
        }
It goes in the same error.
Mar 21 at 2:34 PM
SOLVED !!! :

Finally I found how to avoid the Exception. I changed under System.Data.Entity.SqlServer.SqlProviderServices under QueryForManifestToken
From:
 var serverType = sqlVersion >= SqlVersion.Sql11 ? SqlVersionUtils.GetServerType(conn) : ServerType.OnPremises;
To:
var serverType =  ServerType.OnPremises;
I know, is not the best solution but it is working.

Below I'll try to explain how I arrived to this decision.
I have few SQL Servers having different versions. After many attempts I was amazed to see that one works. But more strange than this. If, in the same applications, I read first from that server I can read, after that, from one that wasn't works. Let see the code:
var dbOK = new Traceability ("TraceabilityOK");
var dbNOK = new Traceability ("TraceabilityNOK");

var clientsOK = dbOK.Clients.ToList (); // A)
var clientsNOK = dbNOK.Clients.ToList ();// B)
A) before B) =>OK
B) before A) =>Go in Exception

I used SQL Profiler to see what's the difference. When finished in Exception I saw one query more: "select serverproperty('EngineEdition')"
I looked after this code under EntityFramework source and I decided to non longer run the method GetServerType(DbConnection connection) from System.Data.Entity.SqlServer.SqlVersionUtils. I'm not using Azure.

Maybe someone who knows the source code better can find a better approach.

Thank you !
Mar 21 at 6:04 PM
Hi Liviu,

This is an interesting find. I wonder if this is reproducible without EF. Can you try the following snippet and let me know?
using (var connection = new SqlConnection(connString))
{
    connection.Open();

    foreach (var cmdText in new[] {"select serverproperty('EngineEdition')", "SELECT 1"})
    {
        using (var command = new SqlCommand(cmdText, connection))
        {
            using (var reader = command.ExecuteReader())
            {
                reader.Read();
                Console.WriteLine(reader.GetInt32(0));
            }
        }
    }
}
This is basically what EF does to detect if we are running against Sql Azure which has some requirements the on premise Sql Server versions don't have. If you are able to repro the problem with the above snippet it would mean that the bug is somewhere between Mono and SqlServer or is a result of a specific SqlServer configuration/security settings (I am not an expert in this area so probably won't be able to help much beyond this)

Another good news is that to workaround the problem you don't actually have to change the EF code. EF6 allows configuring services and one of the services allows overriding the default behavior of getting the provider manifest token. You can register your custom manifest token resolver using code based configuration and either return a hardcoded value (e.g. if you are running always against SqlServer 2012 you would just return "2012") or come up with the version on your own. This should prevent EF from checking what database version your app is running against. If you run against different versions of Sql Server you can use DbConnection.ServerVersion property to create a provider manifest token for your database. Here is an example - you can just copy/paste and it should work because EF should detect the configuration automatically (as long as the DbConfiguration derived class is in the same assembly as your context)
public class Configuration : DbConfiguration
{
    public Configuration()
    {
        SetManifestTokenResolver(new ManifestTokenResolver());
    }
}

public class ManifestTokenResolver : IManifestTokenResolver
{
    public string ResolveManifestToken(DbConnection connection)
    {
        // The simplest thing is just to return hardcoded value
        // return "2012";

        try
        {
            connection.Open();

            var majorVersion =
                Int32.Parse(connection.ServerVersion.Substring(0, 2), CultureInfo.InvariantCulture);

            if (majorVersion == 9)
            {
                return "2005";
            }

            if (majorVersion == 10)
            {
                return "2008";
            }

            return "2012";

        }
        finally 
        {
            if (connection.State == ConnectionState.Open)
            {
                connection.Close();
            }
        }
    }
}
Thanks,
Pawel
Mar 21 at 10:58 PM
Hi Pawel,

You are right. The problem is somewhere between Mono and SQL Server. I was able to replicate the issue using your snippet.
The workaround you gave it to me works great too.

Thank you very much !

Liviu
Mar 22 at 12:14 AM
I am glad to hear that the work around worked.

Since you were able to reproduce the bug in your environment - can you file it in the mono bug tracking system (I believe it is here http://www.mono-project.com/Bugs)? You can provide them with the snippet and a link to this discussion.

Thanks,
Pawel
Mar 22 at 10:34 AM
I reported the issue on Xamarin's bug tracking system: https://bugzilla.xamarin.com/show_bug.cgi?id=18534

Thank you,

Liviu
Mar 22 at 10:42 AM
Serverproperty returns sql_variant, maybe changing the sql to something like
SELECT CAST(SERVERPROPERTY('EngineEdition') AS int) 
would make it work on mono?
Mar 22 at 12:21 PM
Hi Erik,

I tested the query using CAST in the same environment (Mono-Linux) and it's working.
So without CAST it is working in Mono under Windows but not under Linux. With CAST it's working in both.
Maybe is better to be included on System.Data.Entity.SqlServer.SqlVersionUtils in GetServerType(DbConnection connection) method.

Thank you,

Liviu
Mar 22 at 1:57 PM
Great, I think a fix for this should be added to the EF codebase, suggest you log an issue with the proposed solution, and I will be happy to contribute a fix.
Mar 22 at 2:25 PM
I opened the issue 2168.

Thank you,

Liviu
Mar 24 at 9:28 PM
@LiviuShiva - thanks for filing the bugs and confirming a possible fix.
@ErikEJ - nice catch and thanks for the idea for the fix.