Mim

Select * from Seb where Sujets in (SQL Server 2008, ADO.NET, Visual Studio 2008)

Sql Server CE, Multi Threads, Multi User

Et voilà, je viens d'y passer une journée (encore ...) et non, Sql Server CE, n'est PAS multi thread safe...

Alors voilà soyons clair :

Feature

SQL CE

Size of total deployment

1,834 KB

Number of concurrent connections

256

Concurrent process connections

1

Database Size Limit

4GB

Max CPUs Supported

1

Alors la conclusion est consternante, mais :

  • Oui, vous pouvez ouvrir plein de connections simultannément
  • Non, vous ne pouvez pas ouvrir 2 connections sur 2 threads séparés..

Maintenant, un exemple que je viens de faire.

Une petite application où je teste 3 cas. Dans chacun des cas, je fais 1 appels sur 2 services différents. Chaque service me renvoyant des données provenant d'une base de donnée Sql Server CE.

les 3 cas que je veux tester :

  1. Apel des 2 services en synchrone, histoire de vérifier que ça marche dans un cas classique mono thread
  2. Appel de mes 2 services par WCF, en monde Asynchrone (via les proxys générés et leurs appels asynchrones générés)
  3. Appel de mes 2 services directement, et utilisation de BackgroundWorkers

L'application est architecturée de manière classique.

  1. Une UI
  2. Un Service WCF
  3. Un projet Entité, partagé par le service et l'UI

 image

Je fais un peu de WCF, mes services sont exposés et je génère les proxys des services via un "Add Service Reference" :

image

Note : Je demande à générer les appels asynchrones, sinon aucun intéret de tester le multi-threading :)

Petit Hack dans mon architecture : Pour le 3eme cas, je référence directement mon assembly service dans mon projet UI (Rhoooo c'est pas bien; mais c'est pour l'exemple :))

Pour information, j'ai utilisé Linq To Sql pour générer le dbml dans le projet Entités, mais pour être absolument sûr du résultat, j'utilise une bonne vielle connection ADO.NET et je génère les commandes à la main.

Cas 1 : WCF Synchrone

Je crée mes 2 services (mes proxys) :

// For Exemple 1 and 2 : use of WCF Services
public ClientProxy.ClientServicesClient ClientServices { get; set; }
public ParameterProxy.ParameterServicesClient ParameterServices{ get; set; }

Je les instancie au démarrage :

ClientServices = new SqlServerCE_WCFAsync.ClientProxy.ClientServicesClient();
ParameterServices = new SqlServerCE_WCFAsync.ParameterProxy.ParameterServicesClient();

J'appel mes 2 services :

this.dataGridView1.DataSource  = ClientServices.GetClients2();

var p = ParameterServices.GetParameter("AddressMail");
if (p != null)
{
    label1.Text = p.Value;
}

Bon, ben ça, ça fonctionne...

Continuons..

Cas 2 : WCF Asynchrone

Mes 2 services sont créés (voir Cas 1)

Je configure le service Client, pour qu'il utilise son proxy de manière asynchrone :

ClientServices.GetClients2Completed += ClientServices_GetClients2Completed;

je crée la méthode Completed :

void ClientServices_GetClients2Completed(object sender, 
             SqlServerCE_WCFAsync.ClientProxy.GetClients2CompletedEventArgs e)
  {
      this.dataGridView1.DataSource = e.Result;
  }

Et je tente d'appeller ce service en asynchrone, alors que j'appel le service Parameter en synchrone lui :

ClientServices.GetClients2Async();
var p = ParameterServices.GetParameter2("AddressMail");
if (p != null)
{
    label1.Text = p.Value;
}

Et là ça se vautre lamentablement avec une exception qui ferait frémir un mamouth congelé :

"

A first chance exception of type 'System.AccessViolationException' occurred in System.Data.SqlServerCe.dll
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

"

Cas 3 : Utilisation de BackGroundWorker

Ici, je me passe de WCF (et donc je référence directement l'assembly service dans mon projet UI)

et je tente d'utiliser un BackGroundWorker sur le service ClientServices et je fais un appel direct sur le service ParameterServices

Configuration du BackGroundWorker :

bgw1 = new BackgroundWorker();
bgw1.DoWork += new DoWorkEventHandler(bgw1_DoWork);
bgw1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw1_RunWorkerCompleted);

Configuration du travail à effectuer :

void bgw1_DoWork(object sender, DoWorkEventArgs e)
 {
     SqlServerCE_WCFServices.ClientServices service = new SqlServerCE_WCFServices.ClientServices();

     e.Result = service.GetClients2();

 }

Configuration du completed du BackGround Worker :

void bgw1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  {
      if (e.Error != null)
          Trace.WriteLine("Erreur in User Interface in GetClients. Exception : " + e.Error.Message);

      if (e.Cancelled)
          Trace.WriteLine("Action cancelled in User Interface in GetClients");

      this.dataGridView1.DataSource = e.Result as List<Client>;

  }

Appel :

bgw1.RunWorkerAsync();

SqlServerCE_WCFServices.ParameterServices service = new SqlServerCE_WCFServices.ParameterServices();
var p = service.GetParameter2("AddressMail");
if (p != null)
{
    label1.Text = p.Value;
}

Et là, c'est le drame ...

"

A first chance exception of type 'System.AccessViolationException' occurred in System.Data.SqlServerCe.dll
Attempted to read or write protected memory. This is often an indication that other memory is corrupt
.

"

Conclusion

Alors voilà, conclusion:

  • Oui, utilisez un BackGround Worker pour faire des appels asynchrone et rendre votre application moins freezée et plus agréable
  • Non, n'utilisez pas d'appel asynchrone sur plusieurs threads...

Et là, je demande, comment faire avec WCF ?? Le monsieur génère un Thread par Service Proxy... c'est balôt !

Et je demande encore, comment faire une opération (de maintenance, ou de synchronisation) en arrière plan dans mon application desktop sous SQL Server CE ? Je demande à mon utilisateur d'être sympa et de pas faire de requêtes en attendant ??

Je laisse le code à disposition si le coeur vous tente :)

Commentaires

styx31 a dit :

Et ouaip, c'est très naze :(

Ayende en parle sur son blog, lorsqu'il a cherché à utiliser un SGBD embarqué pour un projet : www.ayende.com/.../In-search-of-an-embedded-DB.aspx

Un peu consternant qd même...

# août 5, 2008 5:01

Mimetis a dit :

J'ai trouvé Styx, je poste la solution demain ... :)

# août 5, 2008 5:16

Mim a dit :

Ce post fait suite au post d&#39;hier , au sujet du multi threading avec Sql Server CE Dans l&#39;exemple

# août 6, 2008 9:37