Erreur 0x80073cf6 après avoir rétrogradé de Windows 10 vers Windows 8.1

Nous avons eu une problématique récemment avec un de nos clients qui a voulu tester Windows 10 sur sa tablette Surface Pro 3 pour vérifier que l’application Windows Store Apps que nous lui avons développée pour Windows 8.1 fonctionne toujours correctement.

En l’occurrence, l’application fonctionnait correctement, mis à part les modifications effectuées par Microsoft avec Windows 10 sur la nouvelle interface de CameraCaptureUI qui est maintenant dans une petite fenêtre. Il a donc décidé de rétrograder de Windows 10 vers Windows 8.1. La rétrogradation étant faite dans le mois suivant l’installation, elle n’était pas compliquée en suivant les informations pour Désinstaller Windows 10 et revenir à Windows 8.1.

2015_11_5-steps-to-downgrade-from-microsoft-windows-10-to-windows-81-or-windows-7

Lire la suite

Conférence Microsoft sur Windows 10 à suivre en direct aujourd’hui

Comme vous le savez certainement, Microsoft fait une conférence de présentation de tout ce que l’on peut attendre sur Windows 10 ce soir, mercredi 21 Janvier 2015 à 18h (heure française).

Pour suivre la conférence en direct, rendez-vous sur http://news.microsoft.com/windows10story/.

Ici chez Soft’it, on attend beaucoup de cette conférence et des nouveautés de Windows 10 :

  • Le retour du menu démarrer
  • Les Universal Apps
  • Les « Modern » Apps qui ne sont plus obligatoirement en plein écran
  • Les améliorations faites pour les entreprises

Et vous, qu’attendez-vous de cette conférence ?

[MVC] Utilisation de jQuery DataTables en mode serveur

Note: le projet est disponible sur GitHub : https://github.com/MarienMonnier/softit-jquerydatatables-demo

Dans cet article, nous allons voir comment le plus simplement possible, vous allez pouvoir utiliser jQuery DataTables (version 1.10 minimum) en mode serveur, pour récupérer vos données via ASP.Net MVC 5.

Il existe déjà plusieurs projets qui montrent comment utiliser ASP.Net MVC avec jQuery DataTables, mais au final, dans tous les exemples que j’ai trouvé, ils utilisaient tous un custom IModelBinder pour pouvoir faire le binding des différentes propriétés avec un View Model. Sauf qu’en utilisant MVC 5 et jQuery DataTables 1.10 (et supérieur), ce custom binding n’est pas nécessaire, et il enlève une bonne épine du pied.

La mise en place d’une DataTables en mode serveur avec ASP.Net MVC va se faire en plusieurs étapes :

  1. Création des View Models
  2. Création de la vue
  3. Création du Controller et des services

Et parce qu’il est toujours intéressant de pouvoir ajouter des filtres personnalisés, nous finirons par :

  1. Ajout de filtres supplémentaires

Création des View Models

DataTables envoie et récupère des données sous un certain format qui lui est spécifique. À partir des informations fournies par la documentation, nous récréons d’abord les View Models.

Le View Model pour le résultat global :

public class DTResult<T>
{
    public int draw { getset; }
    public int recordsTotal { getset; }
    public int recordsFiltered { getset; }
    public List<T> data { getset; }
}

Et le View Model des paramètres qui sont envoyés par DataTables à la requête AJAX. La seule propriété qui ne fait pas partie de la requête est « SortOrder », qui est une propriété maison permettant de faire un tri sur nos données via la méthode d’extension SortBy<T> de l’objet IQueryable<T>.

public class DTParameters
{
    public int Draw { getset; }
    public DTColumn[] Columns { getset; }
    public DTOrder[] Order { getset; }
    public int Start { getset; }
    public int Length { getset; }
    public DTSearch Search { getset; }
    public string SortOrder
    {
        get
        {
            return Columns != null && Order != null && Order.Length > 0
                ? (Columns[Order[0].Column].Data + (Order[0].Dir == DTOrderDir.DESC ? " " + Order[0].Dir : string.Empty))
                : null;
        }
    }
}

Création de la vue

Dans le cadre de cet exemple, nous allons afficher une liste de produits de la base Northwind récupérés avec les différents appels AJAX de DataTables.

Tout d’abord la partie HTML (« bootstrapée »):

<div class="panel panel-default">
    <div class="panel-heading">Product Search</div>
    <table class="table table-striped table-hover table-bordered table-responsive" id="dt" width="100%">
        <thead>
            <tr>
                <th data-column="ID" data-order="desc">ID</th>
                <th data-column="Name">Name</th>
                <th data-column="UnitPrice">UnitPrice</th>
            </tr>
        </thead>
    </table>
</div>

Ensuite, la partie JavaScript qui fait appel à DataTables. Il n’y a rien de particulier dans ce code, c’est la configuration de base de DataTables pour :

  • faire les appels en mode serveur (processing: true et serverSide: true)
  • lui indiquer que la page à appeler est notre méthode GetData de notre HomeController
  • configurer les colonnes à afficher.
<script type="text/javascript">
    $(function() {
        var table = $('#dt');
        var dt = table.dataTable({
            ajax: {
                type: 'POST',
                url: '@Url.Action("GetData""Home")'
            },
            columns: [
                { data: 'ID' },
                { data: 'Name' },
                { data: 'UnitPrice' }
            ],
            order: [0, 'desc'],
            processing: true,
            serverSide: true,
            orderMulti: false
        });
    });
</script>

Création du Controller et des services

La table est maintenant configurée pour appeler l’action GetData du HomeController.

Notre contrôleur a une méthode GetData qui :

  • prend en paramètre le View Model DTParameters que nous avons écrit et qui contient tous les paramètres que DataTables nous envoie
  • fait appel à un service (GetProducts) pour récupérer les produits en fonction de ces paramètres
  • fait appel à un second service (Count) pour récupérer le nombre d’éléments suivant ces paramètres (pour la pagination)
  • retourne les données au format JSON telles qu’attendues par DataTables (un DTResult<Product>).
public JsonResult GetData(DTParameters dtModel)
{
    try
    {
        List<Product> data = new ProductService().GetProducts(dtModel.Search.Value, dtModel.SortOrder, dtModel.Start, dtModel.Length);
        int count = new ProductService().Count(dtModel.Search.Value);
        DTResult<Product> result = new DTResult<Product>
                            {
                                draw = dtModel.Draw,
                                data = data,
                                recordsFiltered = count,
                                recordsTotal = count
                            };
        return Json(result);
    }
    catch (Exception ex)
    {
        return Json(new { error = ex.Message });
    }
}

Et les méthodes de service associées :

public class ProductService
{
    private static readonly List<Product> Products;
 
    static ProductService()
    {
        Products = new List<Product>
                    {
                        new Product(1, "Chai", 18.0000m),
                        new Product(2, "Chang", 19.0000m),
                        // ...
                        new Product(76, "Lakkalikööri", 18.0000m),
                        new Product(77, "Original Frankfurter grüne Soße", 13.0000m)
                    };
    }
 
    public List<Product> GetProducts(string search, string sortOrder, int start, int length)
    {
        return FilterProducts(search).SortBy(sortOrder).Skip(start).Take(length).ToList();
    }
 
    public int Count(string search)
    {
        return FilterProducts(search).Count();
    }
 
    private IQueryable<Product> FilterProducts(string search)
    {
        IQueryable<Product> results = Products.AsQueryable();
 
        if (!string.IsNullOrWhiteSpace(search))
            results = results.Where(p => p.Name.Contains(search));
 
        return results;
    }
}

Les services sont plutôt simples :

  • une méthode pour récupérer une liste de produits suivant une recherche, un tri, et une pagination
  • une méthode qui permet de compter le nombre d’éléments suivant le filtre donné (pour que la pagination soit effective côté jQuery DataTables).

Nous avons à présent une démo fonctionnelle qui nous permet de lister les produits de manière paginée, de changer le nombre d’éléments par page, et de rechercher un produit par son nom :

Ajout de filtres supplémentaires

Il peut toujours être intéressant de passer des données supplémentaires à vos appels AJAX qui sont faits par DataTables, ne serait-ce que pour ajouter des filtres, passer un ID supplémentaire, etc…

Toutes ces données sont à ajouter dans le paramètre data de DataTables.

On va modifier un peu la vue et le JavaScript pour ajouter un panel de filtre sur nos prix minimum et maximum; et un bouton pour lancer la recherche.

<div class="panel panel-default">
    <div>...</div>
    <div class="panel-body">
        <div class="well">
            <div class="row">
                <div class="col-xs-4">
                    <label for="minPrice">Minimum Price</label>
                    <input type="text" name="minPrice" id="minPrice" value="" class="form-control" />
                </div>
                <div class="col-xs-4">
                    <label for="maxPrice">Maximum Price</label>
                    <input type="text" name="maxPrice" id="maxPrice" value="" class="form-control" />
                </div>
                <div class="col-xs-2">
                    <button id="search" class="btn btn-primary"><span class="glyphicon glyphicon-search"></span></button>
                </div>
            </div>
        </div>
    </div>
    <table>...</table>
</div>

Nous passons les filtres dans la fonction data de DataTables, et rafraîchissons la table lorsque l’on clique sur le bouton de recherche :

var dt = table.dataTable({
    ajax: {
        type: 'POST',
        url: '@Url.Action("GetData""Home")',
        data: function(d) {
            d.MinPrice = $('#minPrice').val();
            d.MaxPrice = $('#maxPrice').val();
        }
    },
    columns: [ ... 
});
$('#search').click(function(e) {
    e.preventDefault();
    dt.fnDraw();
});

Nous avons maintenant une vue qui nous permet de sélectionner un prix minimum et maximum. Mettons à jour le Controller et les services pour prendre en compte ces nouvelles données.

Nous passons dans l’appel AJAX deux nouvelles propriétés (MinPrice et MaxPrice), que nous allons mettre dans une classe FilterViewModel très simple :

public class FilterViewModel
{
    public decimal? MinPrice { getset; }
    public decimal? MaxPrice { getset; }
}

Et nous faisons évoluer notre méthode GetData du Controller pour prendre aussi un FilterViewModel en paramètre, et passer les valeurs à nos deux méthodes de service :

public JsonResult GetData(DTParameters dtModelFilterViewModel filterModel)
{
    try
    {
        List<Product> data = new ProductService().GetProducts(dtModel.Search.Value, dtModel.SortOrder, dtModel.Start, dtModel.Length, filterModel.MinPrice, filterModel.MaxPrice);
        int count = new ProductService().Count(dtModel.Search.Value, filterModel.MinPrice, filterModel.MaxPrice);
        DTResult<Product> result = new DTResult<Product>
                            {
                                draw = dtModel.Draw,
                                data = data,
                                recordsFiltered = count,
                                recordsTotal = count
                            };
        return Json(result);
    }
    catch (Exception ex)
    {
        return Json(new { error = ex.Message });
    }
}

Faisons évoluer nos méthodes de service pour y ajouter un filtre sur le prix minimum et maximum :

public List<Product> GetProducts(string search, string sortOrder, int start, int lengthdecimal? minPrice, decimal? maxPrice)
{
    return FilterProducts(search, minPrice, maxPrice).SortBy(sortOrder).Skip(start).Take(length).ToList();
}
 
public int Count(string searchdecimal? minPrice, decimal? maxPrice)
{
    return FilterProducts(search, minPrice, maxPrice).Count();
}
 
private IQueryable<Product> FilterProducts(string searchdecimal? minPrice, decimal? maxPrice)
{
    IQueryable<Product> results = Products.AsQueryable();
 
    if (!string.IsNullOrWhiteSpace(search))
        results = results.Where(p => p.Name.Contains(search));
 
    if (minPrice.HasValue)
        results = results.Where(p => p.UnitPrice >= minPrice.Value);
 
    if (maxPrice.HasValue)
        results = results.Where(p => p.UnitPrice <= maxPrice.Value);
 
    return results;
}

Et voilà !

Nous avons maintenant une démo qui nous permet avec jQuery DataTables 1.10 de faire des requêtes serveur sur notre site MVC 5 en ajoutant des filtres personnalisés, tout cela en quelques minutes seulement.

Pour rappel, ce projet se trouve sous GitHub à l’adresse suivante : https://github.com/MarienMonnier/softit-jquerydatatables-demo

Et, comme je le signalais au début de l’article, il existe déjà plusieurs projets qui ont été faits pour MVC / jQuery DataTables; projets qui peuvent vous intéresser si vous utilisez des versions précédentes de MVC et/ou jQuery DataTables :

[MVC] Corriger les liens CSS relatifs dans un StyleBundle

Récemment, pour un nouveau projet en MVC 5, nous avons utilisé Bootstrap et jQuery DataTables.

Afin de mieux s’intégrer à « l’esprit » Boostrap, DataTables intègre un fichier JavaScript (dataTables.bootstrap.js) et un fichier CSS (dataTables.bootstrap.css).

Nous avions décidé d’ajouter ce fichier CSS à un StyleBundle dans BundleConfig.RegisterBundles :

bundles.Add(new StyleBundle("~/Content/bootstrap-datatable").Include(
            "~/Content/DataTables-1.10.0/css/dataTables.bootstrap.css"));

En local tout fonctionnait correctement, mais une fois sur le serveur d’intégration, les images de tri de DataTables n’apparaissaient pas. En regardant avec l’outil de développeur de Chrome, on peut voir que nous avions des erreurs 404 sur nos images :

Failed to load resource: the server responded with a status of 404 (Not Found) http://xxx/images/sort_desc.png

Après analyses, nous avons détecté que dataTables.bootstrap.css spécifie des chemins relatifs pour ses images :

table.table thead .sorting { backgroundurl('../images/sort_both.png') no-repeat center right; }
table.table thead .sorting_asc { backgroundurl('../images/sort_asc.png') no-repeat center right; }
table.table thead .sorting_desc { backgroundurl('../images/sort_desc.png') no-repeat center right; }

Le navigateur Web recherche donc les images en relatif par rapport au chemin de notre Bundle (~/Content/boostrap-datatable) et non pas en fonction du chemin relatif du fichier css (~/Content/DataTables-1.10.0/css/dataTables.boostrap.css).

Pour régler ce problème, la solution est très simple : il suffit de passer un IItemTransform à votre StyleBundle, et plus spécifiquement, le CssRewriteUrlTransform qui réécrit automatiquement les URLs des fichiers CSS inclus dans le StyleBundle.
Il vous suffit juste d’ajouter le CssRewriteUrlTransform à votre Include:

bundles.Add(new StyleBundle("~/Content/bootstrap-datatable").Include(
  "~/Content/DataTables-1.10.0/css/dataTables.bootstrap.css"new CssRewriteUrlTransform()));

A présent, nos images ont réapparu, et nous n’avons plus d’erreur 404. En analysant le CSS généré avec Chrome, on peut voir que les URLs du bundle ont été réécrites en fonction du chemin du fichier CSS :

table.table thead .sorting_desc {
    backgroundurl(/Content/DataTables-1.10.0/images/sort_desc.png) no-repeat center right;
}

Edit du 1er octobre 2014 :

Si vous utilisez une application dans un virtual directory (autre que dans la racine /), cette solution ne fonctionne pas. En effet, il n’ajoute pas le préfixe de l’application.

Dans ce cas, vous devez créer votre propre IItemTransform tout en utilisant CssRewriteUrlTransform auquel vous fournissez le chemin corrigé :

public class CssRewriteUrlTransformWrapper : IItemTransform
{
    public string Process(string includedVirtualPath, string input)
    {
        return new CssRewriteUrlTransform().Process("~" + VirtualPathUtility.ToAbsolute(includedVirtualPath), input);
    }
}

Source : http://aspnetoptimization.codeplex.com/workitem/83

Installer une DLL dans la GAC d’un serveur

Pour installer une DLL dans la GAC, les 2 méthodes principales sont les suivantes (d’autres moyens existent mais ne seront pas abordés ici) :

  • Utilisation de gacutil.exe
  • Création d’un installeur MSI
Pour déployer une seule DLL sur votre serveur, la création d’un installeur MSI peut paraître fastidieuse, et de l’autre côté gacutil.exe n’est pas fourni en standalone (il faut installer tout un SDK pour l’avoir; ce qui n’est pas conseillé dans un environnement de production).
La solution la plus simple consiste à récupérer l’exécutable sur votre machine de développement et le copier sur votre serveur :
  1. Identifier le dossier SDK que vous avez installé sur votre machine de développement. Par exemple (la version peut changer):
    C:Program Files (x86)Microsoft SDKsWindowsv7.0ABinNETFX 4.0 Tools
  2. Copier les 2 fichiers suivants sur votre serveur :
    • gacutil.exe
    • gacutil.exe.config
  3. Récupérez la dll gacutlrc.dll qui se trouve dans un dossier de langue du SDK. Suivant la langue de votre SDK, ça pourra être différents dossiers :
    • 1033/gacutlrc.dll (dans le cas d’un SDK en Anglais)
    • 1036/gacutlrc.dll (dans le cas d’un SDK en Français)

    La liste des dossiers en fonction de la langue du SDK : http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx
    Vous pouvez simplement mettre la DLL gacutlrc.dll dans le même répertoire que gacutil.exe sur votre serveur.

  4. À partir de ce moment-là, vous devriez être en mesure d’ajouter une assembly à la GAC sans soucis sur votre serveur :
gacutil.exe /i MonAssembly.dll
Important : Si vous ne copiez pas la DLL gacutlrc.dll, vous pourrez exécuter une commande gacutil.exe /i MonAssembly.dll, mais l’exécutable ne fera rien et aucune aucune erreur ne sera levée !

[SQL Server] Changer de manière permanente le premier jour de la semaine pour un utilisateur

Problématique

Nous avons eu une problématique avec la langue de SQL Server, et plus particulièrement des effets de la langue sur le 1er jour de la semaine.

En effet, dans le cadre d’une requête en utilisant Entity Framework, nous faisions du regroupement par semaine. La plupart des SQL Server de développement sont installés en Français, mais pas le mien, en Anglais. Du coup lorsque j’exécutais la requête, je n’avais pas le même résultat qu’eux (le premier jour de la semaine étant le dimanche et pas le lundi comme eux, la requête me retournait la semaine regroupée à partir du dimanche).

Ce qui faisait planter au passage les tests chez moi, ce qui n’est pas très pratique…

Solution

Il existe toujours la possibilité d’exécuter des commandes T-SQL comme :

SET DATEFIRST 1

ou

SET LANGUAGE FRENCH

L’inconvénient de ces commandes est qu’elles ne fonctionnent que pour la session actuelle. J’ai essayé de le mettre dans le constructeur de notre DbContext, mais sans succès (les tests s’exécutant en parallèle, il n’a pas l’air d’avoir trop apprécié).

J’en suis venu à modifier le langage de mon utilisateur dans SQL Server. Pour le faire, deux options :

  • Via SQL Server Management Studio :
Security / Logins / clic droit sur le login utilisé par entity framework / Properties
Et dans la page de propriétés, changer la propriété « Default language » (dernière option de la page principale)
  • Via une requête T-SQL :
ALTER LOGIN sa WITH DEFAULT_LANGUAGE = french

Source :
http://www.experts-exchange.com/Database/MS-SQL-Server/Q_24751670.html