Quantcast
Channel: kerrubin's blog » NoSQL
Viewing all articles
Browse latest Browse all 3

[C#] Faire communiquer MongoDB avec Ext.Net

$
0
0

On continue dans la lancée de MongoDB !
Cette fois, j’ai voulu coupler MongoDB et Ext.Net.

J’ai voulu faire une petite chose assez simple : une liste d’utilisateurs, avec quelques infos basiques.
Comme je suis réellement super doué en design (joke, hein, c’est pas crédible), j’ai voulu utiliser Ext.Net.
D’un autre coté, avec les performances de MongoDB, je me suis dis que récupérer la liste de mes N utilisateurs dans la page pour les binder au Store, c’est bien, mais pas top.
Donc utilisation d’un handler pour que chaque page soit alimentée avec le minimum (et donc le poids de la page).

C’est là que commence le début des problèmes ^^

 

Situation :

 
J’ai une classe UserInfo qui doit être injectée en base.
Elle contient entre autre Login, FirstName, LastName et Id.
J’ai donc créé mon grid panel :

<ext:GridPanel
    ID="GridPanelUsers"
    runat="server" 
    StripeRows="true"
    TrackMouseOver="true"
    Height="500"
    Anchor="100%"
    Layout="AnchorLayout"> 
    <Store>
        <ext:Store ID="StoreUsers" runat="server" RemoteSort="true" AutoLoad="true">
            <Proxy>
                <ext:HttpProxy Method="GET" Url="~/Handlers/UsersListHandler.ashx" />
            </Proxy>
            <AutoLoadParams>
                <ext:Parameter Name="start" Value="={0}" />
                <ext:Parameter Name="limit" Value="={20}" />
            </AutoLoadParams>
            <Reader>
                <ext:JsonReader Root="Data" TotalProperty="TotalRecords">
                    <Fields>
                        <ext:RecordField Name="Login" Type="String" />
                        <ext:RecordField Name="LastName" Type="String" />
                        <ext:RecordField Name="FirstName" Type="String" />
                    </Fields>
                </ext:JsonReader>
            </Reader>
            <SortInfo Field="Login" Direction="ASC" />
        </ext:Store>
    </Store>
    <TopBar>
        <ext:PagingToolbar ID="ToolbarUsers" runat="server" />
    </TopBar>
    <ColumnModel ID="ColumnModelUsers" runat="server">
        <Columns>
            <ext:Column Header="Login" DataIndex="Login" />
            <ext:Column Header="LastName" DataIndex="LastName" />
            <ext:Column Header="FirstName" DataIndex="FirstName" />
        </Columns>
    </ColumnModel>
</ext:GridPanel>

Et mon handler (uniquement la méthode pertinente) :

public void ProcessRequest(HttpContext context)
{
    context.Response.ContentType = "application/json";
    Int32 start = 0;
    Int32 limit = 20;
    String sort = String.Empty;
    String dir = String.Empty;
    String query = String.Empty;

    if (!String.IsNullOrEmpty(context.Request["start"]))
        start = Int32.Parse(context.Request["start"]);

    if (!String.IsNullOrEmpty(context.Request["limit"]))
        limit = Int32.Parse(context.Request["limit"]);

    if (!String.IsNullOrEmpty(context.Request["sort"]))
        sort = context.Request["sort"];

    if (!String.IsNullOrEmpty(context.Request["dir"]))
        dir = context.Request["dir"];

    Int64 count = 0;
    IList<UserInfo> users = 
        RepositoriesManager.Instance.GetRepository<UserRepository>()
                .GetAll(out count, start, limit);
    context.Response.Write(JSON.Serialize(new Paging<UserInfo>(users, (Int32)count)));
}

(Normalement, il y a au moins un truc qui doit choquer sur ce code [outre la répétition de code que je vais régler], j’y reviens un peu plus loin)
 

Et là, c’est le drâme

 
Mais dès le premier branchement………BAM !
Une exception du type « InvalidCastException » avec pour message « Unable to cast object of type ‘MongoDB.Bson.ObjectId’ to type ‘UserInfo’. »
Ah ouais…
Comme d’habitude, on debug.

Donc, première astuce, quand on sait pas par où commencer (enfin, là, je savais un peu, mais bon…), on active toutes les exceptions.
Attention, ça peut être violent !
Donc, on va dans le menu « Debug » -> « Exceptions… » (ou Ctrl+D, E avec les raccourcis de base).
Et là, on active comme une grosse brutasse tous les « Thrown » (ou « Levé » en FR; note pour plus tard : passer VS en en-us…).
Debug - Exception
Dès qu’au runtime ça pète un tant soi peu, on sera dessus !

Bref, du coup, j’ai repéré que l’exception se situait au niveau de mon
public override bool Equals(object obj)

Le Equals est dans la classe UserInfo, mais l’objet passé est un ObjectId.
Forcément, ça passe pas…

La méthode appelante, ce situait directement dans le handler : JSON.Serialize()

Ok, la méthode fait planter.
On contourne.

 

Résolution du problème

 

Dans le namespace MongoDB.Bson, il y a une méthode d’extension sur les collection : ToJson().
Cool.
Le résultat :

[{ "CreationDate" : ISODate("2012-05-29T16:00:26.297Z"), "Guid" : CSUUID("a261b6e3-3b7b-4484-aaf2-4d5696c68670"), "_id" : ObjectId("4fc4f29a15e97d071c20ceee"), "Login" : "Login #0", "LastName" : "LastName #0", "FirstName" : "FirstName #0", "MailAdress" : "toto@fake.com", "BirthDate" : ISODate("1982-10-19T22:00:00Z"), "AvatarUrl" : "" }, { "CreationDate" : ISODate("2012-05-29T16:00:26.328Z"), "Guid" : CSUUID("cafb609e-0dd7-4728-9a28-c303c9becc8c")]

C’est pas bon…
Vous voyez le problème ?
Ext.Net attend quelque chose comme ça (avec un autre objet similaire simplifié) :

{"Data":[{"Login":"LOG#0","LastName":"LN#0","FirstName":"FN#0"},{"Login":"LOG#1","LastName":"LN#1","FirstName":"FN#1"},{"Login":"LOG#2","LastName":"LN#2","FirstName":"FN#2"}],"TotalRecords":3}

C’est plus clair ?
En fait, la propriété Id ne peut visiblement pas être sérialisée.
Alors, comment on fait ?
On ajoute l’attribut JsonIgnore du namespace Newtonsoft.Json sur les objet de type MongoDB.Bson.ObjectId.
Simple, rapide, efficace.

Par contre, si on a vraiment besoin de l’Id sur l’IHM ?
Je n’ai pas encore de réponse, mais je creuse (si quelqu’un à la réponse, je suis quand même preneur ^^).

 

Paramètres out et paramètres optionnels

 
Sinon, qu’est ce qui devait un peu choquer dans mon code du handler ?
GetAll(out count, start, limit)

Le paramètre out est en premier.
Il vient tout simplement de la signature de ma méthode :

public IList<UserInfo> GetAll(out Int64 count, Int32 skip = 0, Int32 limit = 0)
{
    MongoCursor cursor = Collection.FindAll();
    // Les valeurs 0 ne sont pas prises en compte, donc aucun skip ni aucune limit
    cursor.SetSkip(skip);   // On skiper les X premiers éléments
    cursor.SetLimit(limit); // On limite le nombre d'éléments retournés à X
    count = cursor.Count(); // On compte le nombre total d'éléments dans le curseur (sans skip ni limit)

    IList<UserInfo> users = new List<UserInfo>();
    foreach (UserInfo item in cursor)
        users.Add(item);
    return users;
}

En fait, les paramètres out doivent être positionnés juste après les paramètres requis (et donc avant les paramètres optionnels).
Si je déplace le out en dernier, j’ai cette erreur de compilation : « Optional parameters must appear after all required parameters ».

A noter également que la méthode Count() sur le curseur renvoie un Int64 (long) tandis que dans le handler pour Ext.Net, le count est un Int32. Il n’y a que peu de chance que ça pète dans ce que je fais, mais la prudence veut quand même qu’on prévoit un minimum (plafonnement du nombre de résultats, par exemple).



Viewing all articles
Browse latest Browse all 3

Latest Images



Latest Images