Sql Server CE, Multi Threads, Multi User. Part II
Ce post fait suite au post d'hier, au sujet du multi threading avec Sql Server CE
Dans l'exemple, nous avons démontrer que nous ne pouvions pas faire de multi threading et que nous remontions de vieilles exceptions pas extraordinaire.
D'ailleurs, j'étais conforté par cette idée, au vu d'un post de Ayende quand celui ci cherchait une base de donnée embarquée.
En fait il existe une solution.
Sql Server CE accepte le multi threading, à partir du moment où chaque thread possède sa propre SqlCeConnection.
Mais me direz vous, ça parait normal que chaque opération crée une nouvelle Connection, réflexe d'utilisation de Sql Server et son pool de connexion.
Nous n'en avons pas sur Sql Server CE.
Et là où c'est balot, c'est que ce n'est ni Sql Server CE, ni mes Services asynchrones qui étaient en erreur .. mais Linq to Sql et ma petite classe static Manager, qui est chargée de me rapatrier une connexion SqlCeConnection.
Pour expliquer ce geste de folie, un DataContext Linq nécessite en constructeur une connexion (SqlCeConnection dans mon cas)
J'avais donc écrit un truc du genre:
private static SqlCeConnection configurationDbConnection;
/// <summary>
///Get Connection From Configuration File if any
/// </summary>
public static SqlCeConnection ConfigurationDbConnection
{
get
{
if(configurationDbConnection != null)
return configurationDbConnection;
ConnectionStringSettings connection =
ConfigurationManager.ConnectionStrings["LocalDatabaseConnection"]
as ConnectionStringSettings;
if (connection == null && ConfigurationManager.ConnectionStrings.Count > 1 &&
ConfigurationManager.ConnectionStrings[1].ProviderName == "Microsoft.SqlServerCe.Client.3.5")
connection = ConfigurationManager.ConnectionStrings[1] as ConnectionStringSettings;
if (connection == null)
throw new ApplicationException("No Connection String in application configuration file");
configurationDbConnection = new SqlCeConnection(connection.ConnectionString);
return configurationDbConnection;
}
}
On voit que je stocke la connection créée dans une variable statique.
Monumentale ERREUR !!
Chacun des threads créant son propre DataContexte, va utiliser la MÊME Connexion qu' un autre thread (static inside)
Pour être sûr que chaque DataContexte crée sa propre connexion, il suffit juste d' appeler son constructeur avec un paramètre non pas une connexion, mais juste une chaîne de connexion.
Un petit coup de reflector, nous montre qu'il va alors bien créer une nouvelle connexion :
provider.CreateConnection() appellant le DbProviderFactory de SqlServerCe, celui ci retournant un joli new SqlCeConnection().
Du coup, mon petit code de mon Manager devient :
private static ConnectionStringSettings connectionStringSettings;
/// <summary>
/// Get Connection From Configuration File if any
/// </summary>
public static SqlCeConnection GetConfigurationDbConnection()
{
if (connectionStringSettings == null)
{
connectionStringSettings =
ConfigurationManager.ConnectionStrings["LocalDatabaseConnection"]
as ConnectionStringSettings;
if (connectionStringSettings == null && ConfigurationManager.ConnectionStrings.Count > 1 &&
ConfigurationManager.ConnectionStrings[1].ProviderName == "Microsoft.SqlServerCe.Client.3.5")
connectionStringSettings = ConfigurationManager.ConnectionStrings[1] as ConnectionStringSettings;
if (connectionStringSettings == null)
throw new ApplicationException("No Connection String in application configuration file");
}
return new SqlCeConnection(connectionStringSettings.ConnectionString);
}
Et là, miracle (ou pas ) tout fonctionne...
Conclusion
Alors, pour faire du multi threading avec Sql Server CE, pensez à une chose importante :
Chaque Thread doit avoir sa propre connexion.
A partir de là, tout roule.
Et si on reprend les caractéristiques de Sql Server CE, on peut créer 256 connexion simultanées, ce qui vous laisse de la marge !