Le Post Infeeny

Les articles des consultants et experts Infeeny

Archives de Catégorie: APPLICATIONS & DIGITAL

Getting started with Azure Search and the .NET SDK

search

In order to provide an alternative to ElasticSearch as a more convenient and straightforward solution, Microsoft introduced Azure Search, an Azure service based on ElasticSearch. Both solutions provide a dedicated environment for indexing and querying structured or semi-structured data. Azure Search, however, focuses on simplicity, to the expense of some of the features you may expect if you come from the more complex engines.

For starters, Azure Search is more rigid: it is contract-based, meaning you have to define the indexes and the structure of your documents (indexed data) before you can index anything. The document structure itself is simplified and aimed at simple use cases and you won’t have all the options ElasticSearch can offer. The most important limitation to keep in mind is that you cannot include complex types in your document structure.

You should really be aware of these limitations when considering Azure Search as a production tool. But if you’re going for a quick, low maintenance and scalable solution, it may very well fit your needs.

Now if you’re still there, let’s start by deploying it on Azure.

Deploying your Azure Search service

Provided you already have an Azure subscription running, setting Azure Search up couldn’t be easier. Just go to the official service page and hit « Try search azure now »… or just click this link.

AzureSearchDeploy

The Azure Search service creation page. Tier prices are removed because they may vary

The service configuration is straightforward: just enter an identifier to be part of the URL of your search service (in the example, « sample-books »), select the usual Azure subscription, resource group and location, and choose the pricing option that best matches your needs. You can even select a free plan if you just want to try it out.

Once you hit the Create button and the service is deployed, we can access its dashboard page and start configuring it.

AzureSearchDashboard

Our new Azure Search service’s Overview page

As you can see there, the service is available right away at the URL provided in the configuration, listed under URL on the overview page.

However, in order to make it operational, we have to create an index. If you’re not familiar with indexes, they are like collections of documents (data) sharing a similar structure, that are processed in order to optimize searches. Because documents live in an index, we are not going anywhere without one.

So let’s get this going by clicking the Add index button.

AzureSearchIndexCreation.png

The Azure Search index creation page

This takes us to another config view where you have to input the name of the index, and the structure of the documents in the index. The structure works pretty much like any graphic database table creation form — you can add fields with a type, and a handful of options that let the engine know how your document should be processed, which in turn affects how the fields behave in search queries.

The mandatory « id » field that comes pre-created will be used as a unique identifier for our documents — if you are trying to index a document and the id has already been indexed, the existing document will be updated.

In our example, each document represents a book. So we set up a few fields that we want indexed for our books.
Here is a quick breakdown of the options for your fields:

  • Retrievable: determines whether the field will be included in the query responses, or if you want to hide it;
  • Filterable: determines the ability to filter on the field (e.g. take all documents with a pageCount value greater than 200);
  • Sortable: determines the ability to sort by the field;
  • Facetable: determines the ability to group by the field (e.g. group books by category);
  • Searchable: determines whether the value of the field is included in full text searches

Our example is set up so that the title, author and description are processed in full search, but not the category. This means that a full text search query for « Mystery » will not include books of the category Mystery in the results.

Once you are done with the creation, your index is ready, although still empty and sad… so let’s fix that!

Indexing data

The next thing to do is indexing actual documents. In our example, this means indexing books.

There are two ways to do this:

  • Adding a data source and an indexer, meaning that Azure Search is going to crawl your data source (Azure Storage, DocumentDB, etc) periodically to index new data;
  • Indexing documents through the REST API, either directly, or indirectly with an SDK.

And of course, nothing prevents you from doing both. But in our case, we are going to index documents programmatically, using C# and the .net Azure Search SDK.

So let’s dig into the coding. As a side note, if you’re allergic to code, you can skip right to the start of the next part, where we play around with Azure Search’s query interface.

First, we’re going to create a console application and add the Azure Search SDK NuGet package.

AzureSearchNugetPackage

Installing the Azure Search SDK through the NuGet Package Manager view on VS2015

Alternatively, you can run the following NuGet command:

> Install-Package Microsoft.Azure.Search

Next, we are going to need a Book POCO class with properties matching the indexed fields. Rather than breaking the naming conventions of C# and using camelCase for our properties, we are going to use the SerializePropertyNamesAsCamelCase attribute to tell the SDK how it is supposed to handle our properties when uploading or downloading documents.

So here is our Book.cs:

[SerializePropertyNamesAsCamelCase]
public class Book
{
    public string Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public string Description { get; set; }
    public string Category { get; set; }
    public int PageCount { get; set; }
    public bool IsAvailableOnline { get; set; }
}

Next, we will need to create a client that connects to our Azure Search service. Using a bit from the official documentation, we can write the following method:

private static SearchServiceClient CreateAdminSearchServiceClient()
{
    string searchServiceName = "sample-books";
    string adminApiKey = "Put your API admin key here";

    SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(adminApiKey));
    return serviceClient;
}

Note that you can find your keys in the Azure Service page, under the « Keys » section. You have to use an admin key in order to create indexes or index documents.
AzureSearchAdminKey.png

Now let’s write a method that indexes a few sample books:

private static void UploadDocuments(ISearchIndexClient indexClient)
{
    var books = new Book[]
    {
        new Book()
        {
            Id = "SomethingUnique01",
            Title = "Pride and Prejudice",
            Author = "Jane Austen",
            Category = "Classic",
            Description = "Set in the English countryside in a county roughly thirty miles from London...",
            IsAvailableOnline = true,
            PageCount = 234
        },
        new Book()
        {
            Id = "SomethingUnique02",
            Title = "Alice's Adventures in Wonderland",
            Author = "Lewis Carroll",
            Category = "Classic",
            Description = "Alice was beginning to get very tired of sitting by her sister on the bank...",
            IsAvailableOnline = true,
            PageCount = 171
        },
        new Book()
        {
            Id = "SomethingUnique03",
            Title = "Frankenstein",
            Author = "Mary Wollstonecraft Shelley",
            Category = "Horror",
            Description = "You will rejoice to hear that no disaster has accompanied...",
            IsAvailableOnline = true,
            PageCount = 346
        }
    };

    // Make a batch with our array of books
    var batch = IndexBatch.MergeOrUpload(books);

    // Query the API to index the documents
    indexClient.Documents.Index(batch);
}

As you can see, the SDK allows us to use directly our Book objects in its upload methods, performing the REST API query for us.

Note that for the purpose of simplicity, we’re not handling exceptions, but you should really do it in production code.

Also keep in mind that your documents will not be instantly indexed. You should expect a little delay between document upload and their availability in index queries. The delay depends on the service load, but in our case a few seconds should be enough.

So let’s set up our program to call these methods and index the books.

static void Main(string[] args)
{
    var serviceClient = CreateAdminSearchServiceClient();
    // Get the index client by name - use your index name here
    var indexClient = serviceClient.Indexes.GetClient("mybookindex");
    UploadDocuments(indexClient);
}

After running the program, if it ran alright and after the aforementioned delay, you should have your data indexed already.

You can check that your documents have been uploaded on the Azure dashboard page.

AzureSearchDocumentsIndexed.png

On the Azure overview page for our service, we can see that there are 3 documents indexed

Alright! When you’re done with the indexing, all that’s left to do is query!

Querying documents

So, our Azure Search service is up and running, with an operational index and some documents to go along. Let’s get to the core feature: querying documents.

For the purpose of illustration and to get a better understanding of what we will be doing next with the SDK, we are going to start with the Azure Search query interface called Search Explorer.

And sure enough, you can access it through the Search Explorer button on the overview dashboard.

AzureSearchExplorer0.png

The Azure Search Explorer default view

The Query string field roughly corresponds to the part after the « ? » in the URL when you query the rest API in a get request.

In the Request URL field below, you can see the full URL that will be called to execute your query.

And finally, the Results field shows the raw JSON response from the service.

Now let’s try it out with some examples:

AzureSearchExplorer2

An example of a full text search

In this example, we are searching for the term « disaster ». This will cause Azure Search to perform a full text search on every field that is marked as Searchable in the index document structure. Because the book « Frankenstein » has the word « disaster » in its description field, and that field is marked as Searchable, it is returned.

If we replace our search term with « Horror », the service returns no results, even though the value of category is literally « Horror » in the case of Frankenstein. Again, this is because our category field isn’t Searchable.

AzureSearchExplorer3

An example of a search using filters

This second example retrieves all books with more than 200 pages. I won’t explain the whole syntax here because there would be too much to write and it is already explained in the search documentation. In essence, we are using the $filter parameter to limit results to the documents satisfying the condition « pageCount gt 200 », which means that the value of pageCount has to be greater than 200 for a document to pass the filter.

Now that we have some clues about how the search API works, we are going to have the SDK do half of the job for us. Let’s go back to our C# .net project.

The first thing we want to start with when querying is a SearchServiceClient… and I know we already built one in part 2, but we are not going to use this one. When you are only querying, you’ll want to use a query API key instead of an admin key, for security reasons.

You can get those keys in the Keys section of the Azure Search service page, after clicking the Manage query keys link.
AzureSearchQueryKeys.png
You are free to use the default one. In my case, I added a new key called « mykey » because I don’t like using an unnamed key and obviously « mykey » is much more descriptive.

So let’s write our new client creation method:

private static SearchServiceClient CreateQuerySearchServiceClient()
{
    string searchServiceName = "sample-books";
    string queryApiKey = "YOUR QUERY API KEY HERE";

    SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(queryApiKey));
    return serviceClient;
}

Of course this is almost the same code as before and we should really refactor it, but I’m leaving that as an exercise to the reader. For the sake of simplicity, of course.

Once we got that, we are going to write the methods that query our books. Let’s just rewrite the tests we have done with the Search Explorer, using the SDK. We will write 2 separate methods, again for the sake of clarity:

private static Book[] GetDisasterBooks(ISearchIndexClient client)
{
    // Query with the search text "disaster"
    DocumentSearchResult response = client.Documents.Search("disaster");

    // Get the results
    IList<SearchResult> searchResults = response.Results;
    return searchResults.Select(searchResult => searchResult.Document).ToArray();
}

private static Book[] GetBooksWithMoreThan200Pages(ISearchIndexClient client)
{
    // Filter on documents that have a value in the field 'pageCount' greater than (gt) 200
    SearchParameters parameters = new SearchParameters()
    {
        Filter = "pageCount gt 200"
    };

    // Query with the search text "*" (everything) and include our parameters
    DocumentSearchResult response = client.Documents.Search("*", parameters);

    // Get the results
    IList<SearchResult> searchResults = response.Results;
    return searchResults.Select(searchResult => searchResult.Document).ToArray();
}

What we can see here is that you have all the URL parameters we can use in the optional SearchParameters object, except the search text itself, which is specified as a separate parameter of the Search method.
And once again, the SDK is capable of using directly our Book class and retrieves Book objects by deserializing the response from our Azure Search service, in a transparent way.

Now let’s use these methods in our program:

static void Main(string[] args)
{
    var queryServiceClient = CreateQuerySearchServiceClient();
    var queryIndexClient = queryServiceClient.Indexes.GetClient("mybookindex");

    var disasterBooks = GetDisasterBooks(queryIndexClient);
    Console.WriteLine("GetDisasterBooks results: " + string.Join(" ; ", disasterBooks.Select(b => b.Title)));
    var moreThan200PagesBooks = GetBooksWithMoreThan200Pages(queryIndexClient);
    Console.WriteLine("GetBooksWithMoreThan200Pages results: " + string.Join(" ; ", moreThan200PagesBooks.Select(b => b.Title)));

    Console.ReadKey(false);
}

The client part is similar to what we did when we were indexing documents, and the rest is just getting the results from the query methods and displaying them with Console.WriteLine.

And running this program gets us this beautiful output:

GetDisasterBooks results: Frankenstein
GetBooksWithMoreThan200Pages results: Pride and Prejudice ; Frankenstein

Going deeper

We have seen how to deploy an Azure Search service, how to create and configure its indexes, and how to use the SDK for both indexing and querying documents. As mentioned in the previous parts, there is a bit more that you can do with Azure Search and that go beyond the scope of this article.

If you want to go further, here are some points we haven’t discussed, along with links providing documentation on the topic:

Thanks for reading and I hope this article at least made you want to read more classical literature.

Mettre en place du déploiement continue en local depuis VSTS

Objectif

L’objectif de cet article est de vous présenter comment mettre en place une chaine de déploiement continue sur un serveur IIS local, hébergé sur votre réseau, à partir de code source stocké dans Visual Studio Team System.

Contexte

Vous stockez  sur VSTS les code sources de vos différents projets applicatifs, mais vous aimeriez mettre en place un chaine de déploiement continue sur vos propres serveurs OnPremise n’ayant pas accès à internet à partir du code source contenu dans votre environnement VSTS.

La solution est de passer par un serveur build intermédiaire qui effectuera aussi le déploiement en local. Ce dernier  aura donc un accès à VSTS et au serveur cible IIS hébergé dans le réseau interne.

VSTS_BuildInterne

Serveur de Build

Nous avons utilisé dans notre cas un serveur de Build avec Windows Server 2012 R2. Mais il est possible d’utiliser le poste de développement en guise de serveur de Build, à condition de respecter les prérequis suivant :

 

L’environnement de développement doit être installé sur le serveur de build et l’agent de build, afin que celui-ci puisse effectuer la compilation du code source contenu dans le VSTS.

Installation et Configuration

Environnement de développement

Il est préférable d’installer et de configurer l’environnement de développement avant de configurer l’agent de build, car ce dernier détecte automatiquement l’environnement du serveur lorsqu’il se connecte à VSTS.

Dans notre cas nous avons installé :

  • Visual Studio 2015 Enterprise, version minimum recommandée
  • NodeJs pour la compilation des less avec gulp
  • Framework 4.6.2

Agent de build

Prérequis de sécurité

Pour installer et configurer l’agent de build, utiliser un compte qui soit à la fois Administrateur du Serveur de build et qui soit administrateur dans VSTS.

Authentification avec un « Personal Access Tokens »
  • Connectez vous sur votre VSTS avec le compte administrateur que vous souhaitez utiliser
  • Ouvrez le profil du compte
  • Allez dans sécurité

PAT_1

  • Allez dans Personal Acces Tokens
  • Cliquez sur PAT_2
  • Cochez seulement le scope Agent Pools (Read, manage), et veillez à laisser les autres décoché

PAT_3.png

  • Cliquez ensuite sur PAT_4

PAT_5

  • Copiez le token ainsi obtenu

Note : Veillez à conserver précieusement le token généré, il sera utile pour configurer et supprimer l’agent.

Installation

Pour installer l’agent sur le serveur de build :

  • Connectez vous sur le serveur de build, avec un compte administrateur
  • Depuis le serveur, connectez vous sur votre VSTS avec un compte administrateur.
  • Allez dans Settings\Agent Pools
  • Cliquez sur  DownLoadAgnet

Une fenêtre contenant les commandes à effectuer pour installer sur différent OS s’ouvre.

DownLoadAgent_2

Lancer le téléchargement à la fin de celui-ci exécuter les commandes PowerShell du bloc Create Agent. Ceci décompressera le zip précédemment téléchargé dans le répertoire C:\agent.

Configuration

Création du pool

Lors de la configuration de l’agent de build, nous allons le rattacher à un pool que nous avons spécialement créé :

  • Connectez vous sur votre VSTS avec un compte administrateur.
  • Allez dans Settings\Agent Pools
  • Cliquez sur « New Pool… » New_Pool
  • Dans la fenêtre de saisie, entrez le nom du nouveau pool : « Interne »

Create_Pool

Agent de build

Nous pouvons passer, maintenant, à la configuration de l’agent de build :

  • Dans la console PowerShell  en tant qu’administrateur, placez vous dans le répertoire où a été installé l’agent
  • Exécuter la commande :Configuration_agent_1

Un assistant de configuration en ligne de commande PowerShell démarre :

Connexion à VSTS :

Configuration_agent_Connection_PS_3
  • Saisissez l’URL VSTS : https://{your-account}.visualstudio.com
  • Sélectionnez le type authentification PAT (précédemment configuré avec le compte que vous utilisé)
  • Saisissez le jeton précédemment généré

Inscription de l’agent sur VSTS :

Configuration_agent_Inscription_PS_4

 

  • Saisissez le nom du pool d’agent précédent créé : Interne
  • Saisissez le nom de l’agent, par défaut il vous propose le nom du serveur : BuildInterne
  • Saisissez le dossier de travail de l’agent : _workVSTS
  • Choisissez de l’exécuter en tant que Service, en saisissant ‘O’
  • Choisissez le compte par défaut

Résultat :

  • Connectez vous sur votre VSTS
  • Allez dans Settings\Agent Queues

Vous  devez voir le résultat suivant :

Configuration_agent_Resultat_VSTS

Vous pouvez utiliser maintenant le pool Interne dans la configuration de vos Builds.

Note : Sur un serveur de build, vous pouvez installer plusieurs agent de build, il faut juste les utiliser des répertoires d’installation différents.

Création d’un Build VSTS

  • Connectez sur VSTS
  • Allez dans « Build & Realease »/Builds

 

Create_Build_1.png

  • Cliquez sur New

Create_Build_2

  • Sélectionnez le Template ASP.NET et cliquez sur « Apply »

Create_Build_3

  • Allez dans l’onglet « Triggers » pour activer l’intégration continue
  • Préciser la branche à surveiller

 

Create_Build_4.png

  • Allez dans l’onglet « Options » pour sélectionner l’agent interne précédemment créé

Create_Build_5.png

  • Allez dans l’onglet « Variables » Afin de modifier :
    • BuildConfiguration selon la configuration souhaitez (Release, Debug, ou une que vous avez créée dans votre solution). Dans notre cas, nous avons créé la configuration de Build « Developpement »

Create_Build_Variables

  • Cliquez sur « Save »

Save_Build_6.png

  • Cliquez sur « Save »

 

Déploiement automatique

L’objectif est déployer automatiquement sur le serveur cible IIS, le dernier package compilé par le pool de build que nous venons de créer avec l’agent « BuildInterne ».

Configuration du serveur Cible

Dans notre cas nous effectuons le déploiement automatique sur un serveur web IIS Windows server 2012 R2.

Sur ce dernier, il faut activer WinRM : Remote Management.

WinRM

Pour activer WinRM sur Windows Server 2012 :

  • Connectez vous sur le serveur Cible
  • Ouvrir sur le serveur Server Manager > Local Server

Server_manager

Site Web Cible

Dans notre exemple, nous allons mettre en place un déploiement automatique sur un site web existant que nous allons créer sur le serveur manuellement. mais cette action est parfaitement automatisable dans le processus de déploiement automatique.

  • Connectez vous sur le serveur cible
  • Ouvrir sur le serveur IIS manager
  • Ajouter un nouveau site Web, par exemple : WebInterne sur un port libre

Note : Pensez bien à ouvrir le port utilisé sur le serveur cible

Créer une release

  • Connectez sur VSTS
  • Allez dans « Build & Realease »/Releases

Create_release_1

  • Cliquez sur « Create release definition »

Create_release_2

  • Sélectionnez Empty et cliquez sur Next
 Create_release_3
  • Sélectionnez votre projet
  • Sélectionnez la définition du build que vous avez précédemment créé
  • Cochez « Continuous Deployement », pour le déploiement s’exécute à chaque nouveau build.
  • Cliquez sur Create

Ajout de tâches

Maintenant que la release a été créé vide, il faut lui ajouter des tâches à exécuter.

Task_release_1.png

  • Modifier le nom de la Release : Interne
  • Liez la release à un package de déploiement :
    • Cliquez sur « Link to an artifact source « Link_to_artefact_release_2.png
  • Sélectionnez la queue de déploiement :
    • Celle que nous avons précédemment créée : Interne

Select_Queue_release_2.png

  • Cliquez sur « Add tasks » :
    • Windows Machine File Copy
      • Ajouter la tache « Windows Machine File Copy », afin de copier le package à déployer sur le serveur cible.Add_Task_Copy_files.PNG
      • Configurez cette tache de copie :
        • Sélectionner le fichier compresser dans le package précédemment lié,Select_Package_release_3
        • Select_Package_release_4
        • Select_Package_release_5
    • WinRM – IIS Web Deployment
      • Ajoutez la tache « WinRM – IIS Web Deployment »Add_Task_Web App Deployment
        • Si cette dernière n’est pas disponible, il faut ajouter l’extension à votre VSTS depuis le market Visual Studio ici.
      • Configurez la tache :
        • WinRM :
          •  Machines : Nom réseau du serveur cible
          • Admin Login/Password : Login et Password d’un compte admin sur le serveur cible
          • Protocol : Sélectionnez le protocole utilisez par le WinRM sur le serveur cible
        • Deploiement :
          • Web Deploy Package : Nom et chemin de copie du package de déploiement, définie dans la tache précédente
          • Website Name : Name du site web précédemment créé : InterneWebApp_release_.png
  • Allez dans l’onglet « Triggers » :
    • Cochez « Continuous Deployment »
    • Sélectionnez le nom du build à surveiller et pour quelle branche le build a été effectuéTriggers_release_.png

 

 

Héberger un package Nuget sur Team Services et l’alimenter depuis une build

Objectif : 

L’objectif de cet article est de vous présenter comment automatiser depuis Team Services la génération d’un ensemble de classes qui seront ajoutées dans un package NUGET hébergé sur TFS online.

Contexte : 

Une solution VS contient un ensemble de classes communes à plusieurs projets.
Ces classes sont regroupées dans des librairies que l’on veut pouvoir accéder depuis NUGET.

vso_nuget_sc1

Création du container sur VSTS : 

Pour créer le container, vous devez aller dans la section « Packages » du menu « Build & Release ». Si vous ne voyez pas le menu, c’est que vous devez préalablement sélectionner un projet.

vso_nuget_sc2

Si vous n’avez pas le menu package depuis votre interface, vous devez aller dans le MarketPlace et installer Package Management (https://marketplace.visualstudio.com/items?itemName=ms.feed)

Vous pouvez alors ajouter un « feed ». Nous vous conseillons de laisser les options par défaut pour le moment.

Une fois créé, il faut modifier les droits de ce dernier. Pour cela aller dans la section « Permission », et ajouter aux contributeurs les comptes « Project Collection Build Service » et « Project Build Service ». Ces deux comptes sont ceux qu’utilisera le Publisher Nuget lors de la Build.

De même vous pouvez restreindre les accès en lecture à certains groupes si vous le désirez.

vso_nuget_sc3

Le container est maintenant créé, il faut l’alimenter.

Création de la Build : 

Nous allons maintenant créer la Build qui générera le package et le mettra dans le feed. Pour cela aller dans Builds et Cliquer sur « New » et choisir une le template Visual Studio.

Dans les étapes, supprimer les modules « Publish symbols path », « Copy Files » et « Publish Articfact », et ajouter les tâches « Nuget Packager » et « Nuget Publisher ».

On peut commencer le paramétrage de chaque étape.

La première consiste à restaurer les packages Nuget nécessaires à la compilation des projets.
Pour cela on sélectionne le path du fichier solution .sln.

vso_nuget_sc4

On procède de la même manière sur l’étape de compilation.

Comme notre solution comporte des tests unitaires, nous laissons l’étape de tests. Ainsi, s’il y a une régression, la build sera en erreur et le package ne sera pas généré.

Concernant l’étape « Nuget Packager », il est possible de spécifier un fichier .sln ; toutes les dlls de la solution seront alors intégrées dans le package, ou préciser un fichier de configuration.
Pour ne pas ajouter des dlls inutiles (celles du framework, celles des librairies de tests), nous faisons le choix de sélectionner un fichier de configuration .nuspec. Ce fichier ajouté manuellement se trouve à la racine de la solution.
Voici le fichier actuellement utilisé :

<?xml version= »1.0″ encoding= »utf-8″?>
<package xmlns= »http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd »&gt;
<metadata>
<id>CommonLibraries</id>
<version>2.0.1</version>
<authors>MCNEXT</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<developmentDependency>true</developmentDependency>
<summary />
<releaseNotes />
</metadata>
<files>
<file src= »0-Services\bin\Debug\0-Entites.dll » target= »lib » />
<file src= »0-Services\bin\Debug\0-Helpers.dll » target= »lib » />
<file src= »0-Services\bin\Debug\0-Repository.dll » target= »lib » />
<file src= »0-Services\bin\Debug\0-Services.dll » target= »lib » />
</files>
</package>

Les données obligatoires sont l’id, le numéro de version et la liste des fichiers.
Le numéro de version est dans ce fichier une valeur fixe. Elle sera écrasée par la build à chaque incrément.

Voici le détail de la configuration :

vso_nuget_sc5

Remarquons que le package généré se trouvera dans le répertoire $(Build.StagingDirectory) et que nous automatisons le versionning en utilisant l’horodatage.

Enfin la dernière étape consiste à publier le package Nuget généré vers le container créé précédemment.
Nous précisons la localisation du package nuget à déployer, tout en supprimant les packages importés lors de la compilation ; le type de Feed, et l’url de ce dernier qui nous a été fournie lors de sa création.vso_nuget_sc6

 

La build est prête, vous pouvez la lancer pour générer votre premier package.

Maintenant nous allons voir comment l’utiliser dans vos solutions.

Importer le package Nuget dans son projet

La première étape est d’ajouter le container à Visual Studio.
Pour cela aller dans Visual Studio, Outils -> Options, ajouter la source de package avec l’url du feed.

Vous pouvez alors ajouter le package Nuget à votre solution/projet en utilisant le package manager de Visual Studio.

vso_nuget_sc8

Il est possible que pour l’installation ou les mises à jour vous ayez besoin de cocher « Include prerelease ».

Importer le package Nuget lors d’une build

Si votre projet consommateur du package Nuget généré est intégré à une build, il faut préciser à l’agent de Build comment récupérer le package pour compiler.

Pour cela, lors de l’étape de restauration du package Nuget, il ne faut pas préciser le path du fichier Solution (.sln) mais plutôt utiliser un fichier de configuration.

Dans chaque solution, nous avons normalement un fichier NuGet.Config dont nous modifions le contenu comme tel :

<?xml version= »1.0″ encoding= »utf-8″?>
<configuration>
<solution>
<add key= »disableSourceControlIntegration » value= »true » />
</solution>
<activePackageSource>
 <add key= »All » value= »(Aggregate source) » />
</activePackageSource>
<packageSources>
<add key= »nuget.org » value= »https://www.nuget.org/api/v2/ &raquo; />
<add key= »CommonLibraries_DEV » value= »https://monurl.pkgs.visualstudio.com/_packaging/CommonLibraries_DEV/nuget/v3/index.json »/&gt;
</packageSources>
</configuration>

Les informations à ajouter sont le paramètre activePackageSource où nous spécifions que nous voulons importer tous les packages.
Et bien sûr, le package à importer.

Dans la configuration d’une build d’un projet, le path est à préciser dans le champs « Path to Nuget.config »

vso_nuget_sc9

 

La build est paramétrée pour importer correctement le package Nuget créé en interne.

 

Nous venons de voir comment configurer un  package Nuget dans VSTS. Cette génération peut très bien être déclenchée dans le cadre d’une intégration continue.

Une étude plus approfondie peut être faite sur la génération du numéro de version pour éviter de rechercher les nouvelles versions du package en cochant ‘pre-release’.

EF6 : Tips&Tricks

EF Core va bientôt prendre la relève mais je pense que EF6 va continué son chemin pendant encore un bon bout de temps avant de disparaître ! C’est pourquoi je me suis dit que j’allais partager avec vous toutes les petites astuces que j’ai découvert ces dernières années.

I. Performances

1) READ_COMMITTED_SNAPSHOT

C’est un paramètre SQL Server au niveau de la base de donnée qui est mis de base en code first, et qui permet « potentiellement » une meilleur scalabilité et moins de deadlocks.

En gros, ça crée un snapshot de la base en read-only sur lequel les SELECT seront executé. Quand un changement s’effectue (par ex : un UPDATE) il s’effectue sur la base (et non sur le snapshot) et un nouveau snapshot de la base est créé.

Ce paramètre est particulièrement adapté lorsque la majorité des requêtes effectuées sont des SELECT.

Commande :

ALTER DATABASE <dbname> SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
ALTER DATABASE <dbname> SET READ_COMMITTED_SNAPSHOT ON;
ALTER DATABASE <dbname> SET MULTI_USER;

2) Warm-up

Lors de la 1ère requête à un Model, EF fait beaucoup de choses avant de pouvoir l’exécuter, cela peut facilement prendre 5-6sec !

Afin d’éviter cela vous pouvez améliorer les choses à plusieurs niveaux, mais personnellement j’ai choisi de contourner le problème en maintenant à chaud mon site sous IIS. Voir cet article de blog pour plus d’infos.

Vous pouvez aussi décider d’utiliser NGEN qui permet d’éviter que l’assembly d’EF (qui est grosse, environ 5Mo) soit JIT à chaque lancement. On peut gagner environ 1-3secondes.

Vous pouvez aussi consulter cet article MSDN pour en savoir plus ce qui se passe et les différentes pistes d’amélioration possibles.

3) Les méthodes async

Sur un site web ou une Web Api le nombre de thread est limité et l’utilisation des méthodes Async de EF permet de libérer le thread courant afin qu’il commence le traitement d’une requête le temps que la base de donnée retourne le résultat.

Plus d’infos sur cet article de blog ou cet article msdn.

4) NoTracking

Lorsque vous faites une requête avec EF, l’état de l’objet est ensuite tracké par le contexte. Régulièrement on n’a pas besoin de ce tracking et cela entraine juste une hausse des consommations CPU/RAM et une perte de performance, surtout dans le cadre de recherche où le nombre d’items retournés est élevé !

Pour éviter de tracker, rien de plus simple, il suffit de rajouter AsNoTracking() à la fin de la query !

Exemple :

var blogs2 = context.Blogs
 .Where(b => b.Name.Contains(".NET"))
 .AsNoTracking()
 .ToList();

5) NCache

NCache est passé OpenSource et peut servir de cache de 2nd niveau pour EF.

6) Break queries

Afin d’améliorer les performances il est souvent préférable de découper une grosse requête en plusieurs petites afin de limiter le nombre d’include.

7) Optimiser les Update via des jointures conditionnelles

Le cas standard d’un update avec EF est le suivant :

L’utilisateur modifie quelques champs -> le client envoie au serveur l’entité entière -> on récupère le graph complet de l’entité côté serveur -> on mappe les champs -> on sauvegarde

Une optimisation courante est donc d’effectuer uniquement les jointures dont on a besoin pour mettre à jour l’entité en base.

La détection du changement peut être faite via le calcul des hashs des entités enfants côté serveur au moment du Get. On recalculera ces mêmes hashs au moment de la sauvegarde et on comparera les hash entre le Get et l’Update pour voir si l’entité enfant doit être mise à jour, et donc si la jointure doit être faite.  Les hashs seront stockés dans une variable au niveau de chaque entité enfants. Pour économiser des cycles CPU serveurs ou de la bande passante ce calcul de hash peut être déporté sur le client.

Exemple

 IQueryable<CLIENT> query = _database.CLIENT; 
 if (clsPersonne.hashIdentiteContactPhysiqueAvant != clsPersonne.hashIdentiteContactPhysiqueApres) 
 query = query.Include(x => x.IDENTITE_CONTACT_PHYSIQUE); 
 ...
 ...
 var client = query.SingleOrDefault(x => x.Id = clsPersonne.Id);

8) Entity Framework Extended Library

Librairie pour EF permettant d’ajouter des fonctionnalités à EF comme les Batch Update/Delete (de base dans EF, on ne peut mettre à jour qu’une ligne par requête).

Permet aussi de gérer un cache simpliste.

9) Au cas où…

N’oublier pas de vérifier vos index et que vos types de données correspondent !

 

2. Les requêtes

1) Mémo pour gérer les graphs d’entités

 

2) Find au lieu de SingleOrDefault

La méthode Find de DbSet permet d’aller récupérer une entité du contexte via sa Primary Key, si l’élément n’est pas trouvé dans le contexte, l’élément est récupéré depuis la base.

Alors qu’avec SingleOrDefault la requête sera toujours exécutée.

3) Logger / Intercepter les requêtes SQL

Une nouveauté peu connue de EF 6.1 est de pouvoir logger les requêtes SQL très facilement, il suffit d’ajouter dans le .config :

<interceptors>
 <interceptor type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework">
 <parameters>
 <parameter value="C:\Temp\LogOutput.txt"/>
 <parameter value="false" type="System.Boolean"/>
 </parameters>
 </interceptor>
 </interceptors>
  • Booléen à False : Le fichier est réécrit à chaque fois que l’application redémarre
  • Booléen à True: Le texte est ajouté à la fin du fichier

4) Func<> vs Expression<Func<>>

Si on souhaite réaliser un filtre, par exemple:

Expression<Func<YourEntity, bool>> FilterByNameLength(int length)

{

return x => x.Name.Length > length;

}

On l’utilise de la manière suivante :

context.Table.Where(FilterByNameLength(500));

Si vous passer une Func<> à une query EF (pour appliquer un filtre par exemple), la méthode ne sera pas traduite en SQL et sera appliquée a posteriori ce qui est rarement ce que l’on souhaite.

Alors que si on utile une Expression<Func<>> la méthode sera bien traduite en SQL.

5) eSQL

Entity SQL (eSQL) est un language indépendant du stockage permettant de récupérer des données sous formes d’objets ou de forme tabulaire. Cela peut être très pratique pour créer des requêtes dynamiques.

Exemple :

using (AdventureWorksEntities context =
    new AdventureWorksEntities())
{
    string esqlQuery = @"SELECT VALUE Contact
        FROM AdventureWorksEntities.Contacts as Contact where Contact.LastName = @ln";

    // The following query returns a collection of Contact objects.
    ObjectQuery<Contact> query = new ObjectQuery<Contact>(esqlQuery, context, MergeOption.NoTracking);
    query.Parameters.Add(new ObjectParameter("ln", "Zhou"));

    // Iterate through the collection of Contact items.
    foreach (Contact result in query)
        Console.WriteLine("Contact First Name: {0}; Last Name: {1}",
                result.FirstName, result.LastName);
}

6) UnitOfWork

C’est un design pattern qui permet d’abstraire toute la couche de persistence tout en évitant de réinstancier le DBContext à chaque opération (ce qui permet aussi de faire des transactions très simplement). Voir cet article msdn pour plus d’informations.

7) DbFunctions

Cette méthode possède des fonctions utilitaires afin d’effectuer des opérations sur les dates au sein des requêtes EF. Voir la doc MSDN.

8) Jointures externes

Pour cela il faut utiliser DefaultIfEmpty, on l’utilise de la manière suivante :

var q = from c in categories

join p in products on c equals p.Category into ps

from p in ps.DefaultIfEmpty()

select new { Category = c, ProductName = p == null ? "(No products)" : p.ProductName };

III. Divers

1) EF Power Tools

C’est une extension Visual Studio qui permet notamment de :

  • Reverse Engineer Code First : Génère le DBContext, les POCO et le mapping Code First à partir d’une base de données existante (option de base dans VS 2015, via l’ajout d’un modèle ADO.Net)
  • Customize Reverse Engineer Templates : Ajoute le template T4 à votre projet afin de pouvoir customiser la génération de code
  • View Entity Data Model (Read-only) : Affiche une vue du modèle Code First dans le EF designer?
  • View Entity Data Model XML : Affiche l’EDMX XML représentant le Code First.
  • Generate Views : Génère des vues précompilées afin d’améliorer le temps de démarrage de l’application

Plus d’infos ici : https://msdn.microsoft.com/en-us/data/jj593170.aspx

2) Pluralization Service

Vous avez déjà dû le remarquer mais EF possède un algorithme pour mettre aux pluriels les variables, il supporte seulement l’anglais mais vous pouvez faire votre service custom et le pluger à Entity Framework.

Voir cet article pour plus d’infos

 

Voilà, j’espère que vous avez appris quelques petits trucs qui vous faciliteront la vie 😉

Tricks : can’t see my LOB app in the Windows Business Store !

If like us you’re desperately trying to publish a new LOB app in your Microsoft Business Store, here is 2 tricks to solve the issue…

Context

Your Windows 10 LOB app is ready to be deployed to your organisation, so you submit it to the store. In the « Pricing and availability » > « Distribution and visibility » section of the submission, you chose « LOB distribution » and checked the store corresponding to your organisation.

Publish LOB app

The certification process is OK, the publication is OK, but the new app doesn’t appear in the Business Store… 😦

Empty Business Store

Trick 1

The first trick is you can check if your app is in the store using the URL https://businessstore.microsoft.com/en-us/store/apps/anything/my_app_storeid (to get the store ID of your app, you open the dashboard of your app and go to App management > App Identity).

Business Store LOB app detail

Trick 2

You see your app thanks to the trick 1, but still not in Manage > New LOB Apps page in the Business Store… So the second trick is to check the language you used to describe your in the submission of you  app to the store. In our case, it was in French only. If I switch from en-us to fr-fr in the URL https://businessstore.microsoft.com/fr-fr/lob/AppDetails, I can now see my app !

Add LOB app to the inventory

And I can add it to the inventory of my private store !

 

#UWPXAML – Compiled Binding – What’s new in the Anniversary Update?

Coming this summer, Windows 10 Anniversary Update will be more or less a Windows 10 v2.0.
With it comes a lot of new things for the windows app development, and a lot of new features and fixes for Compiled Binding.

Today we’ll see what those new features are and how to use them.

If you want to try them out yourself, you’ll need a Windows Insider build of Windows 10 with its Windows SDK and create a UWP project targeting a min version at least equals to Build 14383. Do not install it on your main work station, it will break any non-Anniversary Update apps.

For this post, I’ll use Windows 10 Build 14383 (available on Fast Ring) with Visual Studio 2015 Update 3.
The Windows SDK for Build 14383 can be found here: https://insider.windows.com/

In case you missed it, I wrote a few posts about compiled binding in the past.
Feel free to read them (again).

 

Function binding

Previously, it was possible to directly bind events to methods which followed specific rules regarding their parameters, but we were unable to bind the result of a method to a property without a converter which was calling the method for us.

Now, it’s possible to bind methods directly to properties without the need for a converter!
You can provide whatever method you like as long as it has a public accessor and you provide the parameters.

MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    public string GetHelloWorld()
    {
        return "Hello World";
    }

    public Person Roberts = new Person()
    {
        Firstname = "Dread Pirate",
        Lastname = "Roberts"
    };
}

Person.cs

public class Person
{
    public string Firstname { get; set; }
    public string Lastname { get; set; }

    public string GetFullname()
    {
        return $"{Firstname} {Lastname}";
    }
}

MainPage.xaml

<TextBlock Text="{x:Bind GetHelloWorld()}" />
<TextBlock Text="{x:Bind Roberts.GetFullname()}" />

The method can have parameters.
You can provide them by setting constants in the XAML or by using data from your ViewModel.

MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    public DateTime Today = DateTime.Now;

    public Person Someone = new Person()
    {
        Firstname = "Some",
        Lastname = "One"
    };        

    public string GetPersonFullname(Person person)
    {
        return person.GetFullname();
    }
}

MainPage.xaml

<TextBlock Text="{x:Bind Today.ToString('d', {x:Null})}" />
<TextBlock Text="{x:Bind GetPersonFullname(Someone)}" />

Does the binding is automatically updated when the data is updated?
Well, it is said to work if you set the Mode of the compiled binding to OneWay/TwoWay but I wasn’t able to compile it under VS2015. A few not really understandable compile errors appeared when I used TwoWay binding.

Along that, you may notice some errors appearing in the Error list when using parameters inside your binded functions stating « A value does not fall within the expected range ». It’s a bug of VS2015, but it compiles and runs fine anyway.

I hope those annoying errors will be fixed in the release version.
 

Dictionary indexers

As part of supporting most use cases without the need for a converter, x:Bind now supports access to a specific key in a dictionary.

MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    public Dictionary<string, string> Dictionary = new Dictionary<string, string>()
    {
        { "A", "Alpha" },
        { "B", "Beta" },
        { "C", "Charlie" },
    };
}

MainPage.xaml

<TextBlock Text="{x:Bind Dictionary['A']}" />
<TextBlock Text="{x:Bind Dictionary['B']}" />
<TextBlock Text="{x:Bind Dictionary['C']}" />

It works great but there is a big shortcoming: You can only use constant strings directly set in the XAML.
Using anything else than a string, like an integer, will result in a compile error.
Even string data from your ViewModel won’t work.
 

Explicit value cast

Contrary to classic Databinding which uses duck typing to check if the binding is valid, compiled binding checks at compile time that the given data matches the property to which it is binded to.
In other words, if the value you’re trying to bind has no implicit cast to the type of the property, you can’t bind it using x:Bind without a converter.
Even if the value’s type declares an explicit cast…

As of the Anniversary Update, you will be able to declare an explicit cast inside a compiled binding, C#-style.

MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    public bool? IsThreeStateNull = null;
}

MainPage.xaml

<CheckBox Content="IsThreeState = False" IsThreeState="{x:Bind (x:Boolean)IsThreeStateNull}" />

When casting explicitly, you’ll need to refer to the class type by its XAML namespace.
In this case, Boolean is part of the « x: » namespace declaration.
 

Implicit Visibility conversion

Who don’t know the legendary BooleanToVisibilityConverter?
Everyone, since the first time WPF came around, implemented it over and over in every projects.

x:Bind no longer requires that, it now converts booleans to Visibility by itself! Hurray!
You just need to bind a boolean to a property like Visibility.

MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    public bool IsVisibleFalse = false;
    public bool IsVisibleTrue = true;
}

MainPage.xaml

<TextBlock Text="I'm not visible!" Visibility="{x:Bind IsVisibleFalse}" />
<TextBlock Text="I'm visible!" Visibility="{x:Bind IsVisibleTrue}" />

 

Conclusion

You can find a sample project on GitHub regrouping all the examples we saw in this post: http://github.com/TimLariviere/CompiledBinding-AnniversarySample

Écriture/lecture/modification de fichiers dans un projet Universel app/UWP et cordova

Dans nos applications, nous avons pris le parti d’écrire nos fichiers en JSON. Nous avons donc conçu une abstraction (en WinJS, avec un système de promises) qui nous permet d’utiliser des méthodes claires et simples sur toutes les plateformes (WinRT/UWP ou Cordova (iOS/Android) )

Ce module « DataContainer » est disponible dans notre libraire WinJSContrib (Github)

Dans WinJSContrib.DataContainer, nous avons 4 fichiers qui exposent les mêmes méthodes avec des implémentations différentes :

  • read : lecture d’un élément
  • save : enregistrement d’un élément
  • remove : suppression d’un élément
  • list : liste des fichiers d’un container
  • child : création/accès container enfant

Dans une application WinRT/UWP nous allons naturellement utiliser la couche WinRT/UWP pour écrire/lire les fichiers. Pour cela il suffit d’inclure le fichier « winjscontrib.datacontainer.winrt.file.js »

Dans une application cordova IOS ou Android, nous avons plusieurs choix :

  • Utiliser une base de données : winjscontrib.datacontainer.cordova.database.js
  • Utiliser le système de fichiers avec le plugin file : « winjscontrib.datacontainer.cordova.file.js »
  • Utiliser le localStorage : « winjscontrib.datacontainer.localstorage.js » (utilisable en WinRT aussi)

Ensuite, il faut instancier un container parent et l’utiliser partout dans l’application.

Dans le JS des applications Windows 8/Phone il suffit d’instancier le container (dossier) parent :

MyApp.Data.container = new WinJSContrib.DataContainer.WinRTFilesContainer(undefined, { logger: WinJSContrib.Logger });

Et changer WinRTFilesContainer par notre choix (CordovaDatabaseContainer par exemple) pour l’application cordova en faisant attention de ne l’appeler qu’après l’enclenchement de l’évènement deviceready.

Et c’est tout, la magie s’opère à l’intérieur de notre librairie 🙂

Quelques exemples d’utilisation :

  • Pour lire un fichier :
  • Data.container.read(&quot;objKey&quot;).then(function (data) { }, function (error) { });
    
  • Supprimer un fichier :
  • Data.container.remove(&quot;objKey&quot;).then(function () { }, function (error) { });
    
  • Enregistrer un fichier :
  • Data.container.save(&quot;objKey&quot;,obj).then(function () { }, function (error) { });
    
  • Accès à un container enfant (sous dossier) avec la lecture d’un fichier fichier :
  • Data.container.child(folderid).read(&quot;subObjKey&quot;).then( function (subObjInFolderID) { }, function (error) { }));
    
  • Liste des fichiers dans un container :
  • Data.container.list().then(function (res) {}, function (error) { });
    

Tricks : Gérer son numéro de version au niveau de la solution

Si comme moi, vous avez toujours rêvé de pouvoir gérer son numéro de version au niveau de solution et non au niveau de chaque projet cet article est fait pour vous !

Marche à suivre

  1. Click droit sur la solution -> Ajouter un nouvel item -> Classe Visual C#
  2. Nommez le fichier « SharedAssembyInfo.cs »
  3. Reprenez toutes les infos d’un « AssemblyInfo.cs » d’un projet existant et copier-coller le contenu dans « SharedAssembyInfo.cs ». Les fichiers « AssemblyInfos.cs » se trouvent dans la rubrique « Properties » située juste au-dessus de « Références ».
  4. Mettez en commentaire tout le contenu des différents « AssemblyInfo.cs »
  5. Sur chaque projet :
    1. Click droit -> Ajouter élément existant
    2. Sélectionner le « SharedAssembyInfo.cs »
    3. Choisir d’ajouter en tant que lien
    4. Faites glisser le fichier ajouté au projet dans le dossier « Properties »

A savoir

Vous n’êtes pas obligé de partager toutes les informations dans le « SharedAssembyInfo.cs », tout ce qui est spécifique à chaque projet vous pouvez le laisser dans le « AssemblyInfos.cs » du projet.

Pour allez plus loin

Savez-vous que vous pouvez générer un numéro de build sans usine de build depuis Visual Studio? Pour cela il suffit de :

  1. Mettre en commentaire l’attribut « [assembly: AssemblyFileVersion(« 1.0.0.0 »)]» que l’on trouve dans « SharedAssembyInfo.cs » ou « AssemblyInfos.cs ». Seul l’attribut « AssemblyVersion » doit être spécifié.
  2. Mettre l’attribut « AssemblyVersion » sous la forme : « [assembly: AssemblyVersion(« Majeur.Mineur.* »)] »

Après ça, à chaque build, Visual Studio va remplacer l’étoile par un « numéro de build » et un « numéro de révision » :

  • Numéro de build : Le nombre de jour depuis le 1er Janvier 2000.
  • Numéro de révision : Le nombre de secondes depuis minuit divisé par 2.

 

J’espère que cet article vous aura été utile 😉

 

Sources

http://weblogs.asp.net/ashishnjain/sharing-assembly-version-across-projects-in-a-solution

http://www.csharpcity.com/2012/visual-studio-automatic-version-numbering/

https://msdn.microsoft.com/en-us/library/k49w9389(v=vs.110).aspx

Xamarin – Using SQLite.NET Async with PCL

With Xamarin, it is now easier than ever to share most of the logic between your apps.
Most today’s apps use a lot of data, so deciding how to store them locally can prove a challenge.
SQLite provides a great way to store and query those easily and does support Xamarin. Hurray!

In this post, we’ll see how to setup SQLite in a portable project (PCL) shared by two Xamarin projects (Android, iOS) and a UWP project in Visual Studio.
It should be more or less the same with Xamarin Studio.

If you’re interested in using SQLite.NET with Xamarin.Forms, see the Xamarin.Forms documentation instead.

Portable class library and NuGet packages

First of all, we’ll need to create a portable class library supporting Android, iOS and Windows.
Conveniently, Visual Studio provides a template which exactly fits our needs.

PCL.PNG

If for any reason you can’t or won’t use this template, you can use the generic « Class Library (Portable) » template instead.
See Xamarin’s Introduction to Portable Class Libraries for more information on it.

Then, we’ll need to add references to SQLite.NET via the NuGet package SQLite.NET.Async-PCL v3.1.1 which comes along with its dependency SQLite.Net.Core-PCL.

Nuget1.PNG

That’s all we need to setup the PCL in order to use SQLite.NET.

Using SQLite.NET

Now for the tricky part.

We can now use SQLite in the PCL and thus write it once and be able to use it on each platform. But in order to do so, we’ll need a connection to the SQLite database (and need to create it if it’s not the case).

As we’re targeting three different platforms, each with its own rules on how to access the storage space, SQLite.NET needs several things in order to work : a path to where SQLite should store the database file and a custom implementation of the wrapper around the native sqlite3.

For that, as we’re using SQLite.NET Async, we need to create a SQLiteAsyncConnection.
It’s fairly simple.

public class SQLiteDatabase
{
    public static SQLiteAsyncConnection GetConnection(string path, ISQLitePlatform sqlitePlatform)
    {
        var connectionFactory = new Func<SQLiteConnectionWithLock>(() => new SQLiteConnectionWithLock(sqlitePlatform, new SQLiteConnectionString(path, storeDateTimeAsTicks: false)));
        return new SQLiteAsyncConnection(connectionFactory);
    }
}

See what we did there?
We have created a connection to the SQLite database but all the platform-specific parts are passed as parameters, allowing us to have a common method in the PCL project.

The path parameter is a file path which SQLite will use to store its db file.
The ISQLitePlatform parameter is described in the SQLite NuGet package and represents an internal implementation of SQLite for a given platform. Don’t worry, it has already been done for us for Android, iOS and UWP.

Of course, you can use dependency injection or any architecture that fits your needs instead. The key point here is to leave the implementation detail to the output projects.

We’ll come back shortly after to these platform-specific parameters.

For now enjoy!

public class MyEntityRepository
{
    private SQLiteAsyncConnection _connection;

    public async Task InitializeAsync(string path, ISQLitePlatform sqlitePlatform)
    {
        _connection = SQLiteDatabase.GetConnection(path, sqlitePlatform);

        // Create MyEntity table if need be
        await _connection.CreateTableAsync<MyEntity>();
    }

    public async Task<MyEntity> CreateAsync(string name)
    {
        var entity = new MyEntity()
        {
            Name = name
        };
        var count = await _connection.InsertAsync(entity);
        return (count == 1) ? entity : null;
    }

    public async Task<IEnumerable<MyEntity>> GetAllAsync()
    {
        var entities = await _connection.Table<MyEntity>().OrderBy(m => m.Name).ToListAsync();
        return entities;
    }
}

 

Android / iOS / UWP projects

Xamarin.Android and Xamarin.iOS will both need the exact same configuration regarding SQLite.NET, but UWP will need one extra small step.

Along with the obvious reference to the PCL project, we’ll need to reference the SQLite.NET-PCL v3.1.1 NuGet package as well.
Warning : it is not the same package as the one we used earlier.

This one automatically includes the previously seen ISQLitePlatform implementation of the targeted platform.
When referenced in an output project, it brings another dependency with it where all the platform-specific implementation is present.

  • SQLite.Net.Platform.XamarinAndroid when referenced in a Xamarin.Android project
  • SQLite.Net.Platform.XamarinIOS in a Xamarin.iOS project
  • SQLite.Net.Platform.WinRT in a UWP project

Last step is to use our MyEntityRepository by giving it all the platform specific parameters.

Android

private string GetDbPath()
{
    string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
    return Path.Combine(documentsPath, "Todo.db3");
}

protected override async void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);

    // Set our view from the "main" layout resource
    SetContentView(Resource.Layout.Main);
    var addButton = FindViewById<Button>(Resource.Id.AddButton);
    var todoListView = FindViewById<ListView>(Resource.Id.TodoListView);
    addButton.Click += OnAddButtonClicked;

    _todoRepository = new TodoRepository();

    var path = GetDbPath();

    await _todoRepository.InitializeAsync(path, new SQLitePlatformAndroid());
    var items = await _todoRepository.GetAllAsync();

    todoListView.Adapter = new ArrayAdapter<string>(this, global::Android.Resource.Layout.SimpleListItem1, items.Select(i => i.Text).ToList());
}

private async void OnAddButtonClicked(object sender, EventArgs e)
{
    var todoEditText = FindViewById<EditText>(Resource.Id.TodoEditText);
    var text = todoEditText.Text;
    todoEditText.Text = string.Empty;

    var todoItem = await _todoRepository.CreateAsync(text);
            
    var todoListView = FindViewById<ListView>(Resource.Id.TodoListView);
    var adapter = todoListView.Adapter as ArrayAdapter<string>;
    adapter.Add(todoItem.Text);
}

 

iOS

private string GetDbPath()
{
    string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
    return Path.Combine(documentsPath, "..", "Library", "Todo.db3");
}

public override async void ViewDidLoad()
{
    base.ViewDidLoad();
    AddButton.TouchUpInside += OnAddButtonTouchUpInside;
            
    _todoRepository = new TodoRepository();

    var path = GetDbPath();

    await _todoRepository.InitializeAsync(path, new SQLitePlatformIOS());
    var items = await _todoRepository.GetAllAsync();
            
    TodoTable.Source = new TodoTableDataSource(items, TodoTable);
    TodoTable.ReloadData();
}

private async void OnAddButtonTouchUpInside(object sender, EventArgs e)
{
    var text = TodoTextField.Text;
    TodoTextField.Text = string.Empty;

    var todoItem = await _todoRepository.CreateAsync(text);

    var source = TodoTable.Source as TodoTableDataSource;
    source.Add(todoItem);
}

 

Universal Windows Platform

Remember that one extra small step ? Now is the time to take care of it.

Windows, contrary to Android and iOS, doesn’t have SQLite3 installed by default. So we need to bring sqlite3.dll along in our appx package otherwise we’ll get an exception « Unable to load sqlite3.dll ».
To do so, we’ll need to install the Visual Studio extension for UWP which can be found on the SQLite official website.

Then we just have to add two references in our UWP project.

  • SQLite for Universal Windows Platform (that’s our sqlite3.dll added by the VSIX)
  • Visual C++ 2015 Runtime for Universal Windows Platform (it’s a dependency of the former)

 
Capture.PNG
And that’s all for that extra small step.

Now we can use SQLite like in the Android and iOS projects.

public ObservableCollection<TodoItem> Items { get; set; } = new ObservableCollection<TodoItem>();

private async Task<string> GetDbPathAsync()
{
    var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
    var file = await folder.CreateFileAsync("Todo.db3", CreationCollisionOption.OpenIfExists);
    return file.Path;
}

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    _todoRepository = new TodoRepository();

    var path = await GetDbPathAsync();

    await _todoRepository.InitializeAsync(path, new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT());

    var items = await _todoRepository.GetAllAsync();
    foreach (var todoItem in items)
    {
        Items.Add(todoItem);
    }
}

private async void OnAddTodoItemButtonClicked(object sender, RoutedEventArgs e)
{
    var text = TodoTextBox.Text;
    TodoTextBox.Text = string.Empty;

    var item = await _todoRepository.CreateAsync(text);
    Items.Add(item);
}

 
And there we are! That’s all for today.
Go make some happy users with great apps using SQLite.NET and Xamarin!
 
I’ve put up a sample project on GitHub, it’s a simple Todo list app working on Android, iOS and UWP : https://github.com/TimLariviere/XamarinSQLite

Microsoft Test Manager : Tester un Build

Dans la 1ère partie nous avons vu comment configurer un environnement de test pour Microsoft Test Manager. Nous allons maintenant nous intéresser à comment tester un build sur un environnement configuré et comment faire en sorte que toutes les données qui nous intéresse soient bien collectés, en l’occurence, les données de « Test Impact Analysis« . Ces données permettent de détecter quels tests rejoués en fonction des changement apportés aux code source !

Marche à suivre

  1. Configurer Test Manager pour qu’il se lance toujours en mode administrateur (sinon il n’aura pas les droits pour observer les autres process)
  2. Paramétrer Test Manager (testeur) pour qu’il utilise la configuration créée précédemment en Test settings et la machine où va être déployé l’application comme environnement :
  3. Queue un  XAML build, attendre qu’il finisse. Le premier build va permettre de constituer une base sur laquelle le moteur pourra analyser les différences avec les prochains builds. 
  4. Effectuer le déploiement
  5. Assigner le build au test, une pop-up vous proposant de voir les tests recommandés s’affichera. Assigner un build aux tests permet d’attacher les données de tests à ce build.
  6. Run le Test Plan
  7. Après avoir cliqué sur « Start Test » (pas besoin de coché « Create action recording »), lancer l’application en mode Administrateur afin que le processus tourne avec le même compte utilisateur.
    4
  8. Vérifier à la fin du 1er cas de test que les fichiers « testImpact.xml » et [webServer].testImpactXml sont bien mis en pièce jointe. S’ils ne sont pas présent regarder si vous n’avez pas des fichiers warning afin de déceler des erreurs. 

A Savoir

  • Aucune notification n’apparait si un problème a empêché la collecte d’informations nécessaire à l’analyse
  • Il ne supporte pas bien les multithread/méthodes asynchrone
  • Il détecte correctement le changement de code dans le XAML
  • Test Impact Analysis n’existe pas encore avec le nouveau système de build vNext.
  • Il est préférable qu’un seul application pool utilisant le compte de service tfs et que celui-ci n’ai qu’un seul site web.
  • Le démarrage d’une suite de tests redémarre le serveur IIS

 

Cette procédure fonctionne aussi pour les autres données de tests que l’on peut collecter et rattacher à un build. A ce propos, il faut savoir que pour les données de couverture de code nécessite de labelliser les sources lors du build.  Les données de tests récoltés sont consultables sur le résumé du build dans le portail web tfs (et l’interface est plutôt bien fait !)

J’espère que cette série vous en a appris plus sur les fonctionnalités de Microsoft Test Manager 🙂