<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: François Borgies</title>
    <description>The latest articles on DEV Community by François Borgies (@fborgies).</description>
    <link>https://dev.to/fborgies</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F501079%2Fb5ed1572-1d79-4bb6-b573-b2e898048d0f.jpg</url>
      <title>DEV Community: François Borgies</title>
      <link>https://dev.to/fborgies</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fborgies"/>
    <language>en</language>
    <item>
      <title>Le ShouldSerialize Pattern, un pattern à (re-)découvrir ? </title>
      <dc:creator>François Borgies</dc:creator>
      <pubDate>Sun, 13 Feb 2022 12:02:35 +0000</pubDate>
      <link>https://dev.to/fborgies/le-shouldserialize-pattern-un-pattern-a-re-decouvrir--3ngf</link>
      <guid>https://dev.to/fborgies/le-shouldserialize-pattern-un-pattern-a-re-decouvrir--3ngf</guid>
      <description>&lt;p&gt;Le ShouldSerialize Pattern est une fonctionnalité à utiliser dans le contexte de la sérialisation de&lt;br&gt;
données. Malgré tous les avantages que cette fonctionnalité offre, elle demeure méconnue. Dans cet&lt;br&gt;
article, je vous explique comment en tirer parti et dans quel cadre l’utiliser.&lt;/p&gt;

&lt;p&gt;Le ShouldSerialize Pattern permet de résoudre facilement des problèmes que j’ai rencontrés à de&lt;br&gt;
nombreuses reprises. Le gain de temps potentiel est donc un aspect à ne pas négliger. Afin d’être le&lt;br&gt;
plus concret possible, je vous propose un exemple pratique à partir du postulat suivant :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;J'ai un objet "Personne" à sérialiser en XML. L'objet sérialisé ne doit contenir la date de décès que si elle existe et par conséquent, ne peut pas être égale à la valeur par défaut d'une DateTime (01/01/0001 00:00:00).  Cet objet a la définition suivante :
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[XmlRoot("personne", Namespace = Namespace)]
public class Personne {
     [XmlAttribute("nom")]
     public string Nom { get; set; }

     [XmlAttribute("prenom")]
     public string Prenom { get; set; }

     [XmlAttribute("age")]
     public int Age { get; set; }

     [XmlAttribute("dateDeNaissance", DataType = "date")]
     public DateTime DateDeNaissance { get; set; }

     [XmlAttribute("dateDeDeces", DataType = "date")]
     public DateTime DateDeDeces { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Relativement difficile à résoudre avec une DateTime non-nullable, le pattern ShouldSerialize rend la tâche triviale. &lt;/p&gt;

&lt;p&gt;Comment fonctionne-t-il et comment le mettre en place ? &lt;/p&gt;

&lt;p&gt;On n'a besoin que d'une chose : une méthode facultative (apparue très rapidement dans les premières versions .Net et semi cachée dans la documentation officielle, d'où le fait sans doute de sa disparition dans les bonnes pratiques de développement) liée aux propriétés et qui permet d'avertir le concepteur, Visual Studio dans mon cas, que la propriété a été modifiée par rapport à sa valeur par défaut et qu'il doit en tenir compte, ou non, lors de la sérialisation. &lt;/p&gt;

&lt;p&gt;Reprenons notre objet, dans lequel j'implémente la méthode ShouldSerialize{nom de la propriété}() afin de réaliser la demande business :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[XmlRoot("personne", Namespace = Namespace)]
public class Personne {
     ...

     [XmlAttribute("dateDeDeces", DataType = "date")]
     public DateTime DateDeDeces { get; set; }

     public bool ShouldSerializeDateDeDeces()
     {
         return DateDeDeces != DateTime.MinValue;
     }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Une variante plus récente à partir de C# 7.1, moins lisible à mon sens du fait de l'utilisation du littéral "default", mais renforce la maintenabilité du code :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[XmlRoot("personne", Namespace = Namespace)]
public class Personne {
     ...

     [XmlAttribute("dateDeDeces", DataType = "date")]
     public DateTime DateDeDeces { get; set; }

     public bool ShouldSerializeDateDeDeces =&amp;gt; DateDeDeces != default;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le littéral "default" permet de spécifier la valeur par défaut (merci La Palice) d'un type donné, lorsque le compilateur peut déduire le type d'expression. Dans ce cas, default = DateTime.MinValue. &lt;/p&gt;

&lt;p&gt;Très important dans les deux cas, le nom de la méthode doit commencer par "ShouldSerialize" et être suivi du nom de la propriété visée, "DateDeDeces".&lt;/p&gt;

&lt;p&gt;Au moment où la sérialisation XML sera exécutée, la méthode ShouldSerializeDateDeDeces() sera évaluée par le concepteur, si le retour est "true", la propriété sera intégrée dans l'XML résultant, si le retour est "false", elle ne le sera pas.&lt;/p&gt;

&lt;p&gt;Les avantages de ce pattern sont :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sa facilité d'utilisation, d'écriture et de maintenance sur l'ensemble des propriétés d'un objet à sérialiser, en fonction des demandes business (on sait tous que certaines demandes business peuvent être farfelues ou dépendantes de systèmes rigides et historiques).&lt;/li&gt;
&lt;li&gt;Sa compatibilité pour la plupart des Serializers (XML, JSON, ...).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vous l'avez compris, si votre projet requiert de la sérialisation, le ShouldSerialize Pattern doit figurer en bonne place parmi votre arsenal de fonctionnalités. Par conséquent, gardez bien cette fonctionnalité dans un coin de votre tête ! Il existe d'autres patterns / fonctionnalités que je vous invite à explorer afin de résoudre le plus efficacement possible vos problèmes liés à la sérialisation de données.&lt;/p&gt;

&lt;p&gt;Happy coding !&lt;/p&gt;

</description>
      <category>programming</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>EF Core - Tester efficacement vos Repositories avec xUnit et les tests InMemory.  </title>
      <dc:creator>François Borgies</dc:creator>
      <pubDate>Mon, 23 Nov 2020 09:54:08 +0000</pubDate>
      <link>https://dev.to/fborgies/ef-core-tester-efficacement-vos-repositories-avec-xunit-et-les-tests-inmemory-331b</link>
      <guid>https://dev.to/fborgies/ef-core-tester-efficacement-vos-repositories-avec-xunit-et-les-tests-inmemory-331b</guid>
      <description>&lt;p&gt;Dans cet article je vous montre comment tester efficacement vos repositories avec des tests unitaires InMemory. je recours régulièrement à cette méthode qui donne d'excellents résultats. Il serait dommage de passer à côté ! Cette méthode, simple et efficace, permet de simplifier les tests et d'éviter des effets de bords indésirables.&lt;/p&gt;

&lt;p&gt;Prenons comme exemple la classe User et son repository avec les méthodes de création et de récupération :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class User(){
    public int Id { get; set; }
    public string Lastname { get; set; }
    public string Firstname { get; set; }
}

public interface IUserRepository(){
    public User Get(int userId);
    public int Post(User user);
}

public class UserRepository(){
    private MyDbContext _context;
    public UserRepository(MyDbContext context){
        _context = context;
    }

    public User Get(int userId){
        return _context.Users.SingleOrDefault(u =&amp;gt; u.Id == userId);
    }

    public int Create(User user){
        _context.Users.Add(user);
        _context.SaveChanges();
        return user.Id;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si on souhaite tester unitairement les deux méthodes de ce Repository, il faut Mocker le DbContext et le DbSet.Cette pratique de développement est à éviter. En effet, elle complexifie les tests et, &lt;em&gt;de facto&lt;/em&gt;, en diminue l'efficacité et en augmente le coût de maintenance.&lt;/p&gt;

&lt;p&gt;Une possibilité serait de mocker les méthodes Get et Create et de définir leur comportement, ce qui rend le test pratiquement inutile, car c'est justement le comportement de ces méthodes que l'on souhaite tester. Une solution simple et efficace est d'utiliser l'approche de la base de données en mémoire, comme le montre le code ci-dessous :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class UserRepositoryTests(){
    private readonly Fixture _fixture;

    public UserRepositoryTests(){
        _fixture = new Fixture();   
    }

    [Fact]
    public void GetShouldReturnsUser(){
        // Arrange
        var id = _fixture.Create&amp;lt;int&amp;gt;();

        // Ce code permets de simuler une base de données
        // directement en mémoire et de pouvoir interagir
        // avec celle-ci.   
        var options = new DbContextOptionsBuilder&amp;lt;MyDbContext&amp;gt;()
                .UseInMemoryDatabase("TestIMDatabase")
                .Options;

        using (var context = new MyDbContext(options))
        {
            context.Users.Add(new User
            {
                Id = id,
                Lastname = _fixture.Create&amp;lt;string&amp;gt;(),
                Firstname = _fixture.Create&amp;lt;string&amp;gt;(),
            });
            context.SaveChangesAsync();
        }

        // Act &amp;amp;&amp;amp; Assert
        using (var context = new MyDbContext(options))
        {
            var sut = new UserRepository(context);
            var user = sut.Get(id);
            Assert.NotNull(user);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;J'utilise un vrai DbContext, dans lequel j'insère un User que je récupère via ma méthode de Get. Cela permet de tester unitairement le comportement de cette dernière.&lt;/p&gt;

&lt;p&gt;Même chose pour la méthode Post :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Fact]
public void PostShouldAddUser(){
    // Arrange
    var expectedUser = _fixture.Create&amp;lt;User&amp;gt;();
    var options = new DbContextOptionsBuilder&amp;lt;MyDbContext&amp;gt;()
            .UseInMemoryDatabase("TestIMDatabase")
            .Options;

    // Act &amp;amp;&amp;amp; Assert
    using (var context = new MyDbContext(options))
    {
        var sut = new UserRepository(context);
        var actualId = sut.Create(user);
        Assert.NotNull(actualId);
         Assert.Equal(actualId, expectedUser.id);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce genre de test est très pratique quand on souhaite vérifier le comportement des opérations de type CRUD. Elle l'est tout autant lorsque la base de données en elle-même, n'a pas d'incidence pour le test. En effet, la Base de Données en mémoire est uniquement dédiée aux tests car ce n'est pas une réelle Base de Données, elle ne supporte pas les transactions ni les requêtes SQL en directe et bien sûr, n'est pas optimisée pour la performance.&lt;/p&gt;

&lt;p&gt;Happy coding !&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>efcore</category>
      <category>tutorial</category>
      <category>testing</category>
    </item>
    <item>
      <title>EF Core - Créer un filtre global sur l'ensemble de vos requêtes.</title>
      <dc:creator>François Borgies</dc:creator>
      <pubDate>Fri, 06 Nov 2020 09:46:13 +0000</pubDate>
      <link>https://dev.to/fborgies/ef-core-creer-un-filtre-global-sur-l-ensemble-de-vos-requetes-2h30</link>
      <guid>https://dev.to/fborgies/ef-core-creer-un-filtre-global-sur-l-ensemble-de-vos-requetes-2h30</guid>
      <description>&lt;p&gt;Dans cet article je vous explique comment définir et configurer, à travers l'exemple de la suppression logique, un filtre sur l'ensemble de vos requêtes Linq.&lt;/p&gt;

&lt;p&gt;Avant de rentrer dans le vif du sujet, je vous mets en contexte. Il y a quelques jours, j'ai élaboré un modèle de données qui devait prendre en compte une suppression logique de l'ensemble des entités de ce dernier. J'ai donc naturellement prévu dans le Core de mon application, une classe de base dont héritent directement, ou par transition, les différentes entités de mon modèle :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public abstract class BaseEntity : ISoftDelete
{
    // Implémente ISoftDelete   
    public bool Deleted { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Une fois mon Core défini et mes différents Mappings EfCore créés, je m'attelle à la réalisation des Repositories et des opérations de CRUD. Mais, une chose me saute aux yeux. Pour chaque requête Linq, je me refuse à écrire ce genre de ligne redondante :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Blog Get(Guid blogId)
{
    return _ctx.Blogs.FirstOrDefault(b =&amp;gt; b.Id.Equals(blogId) &amp;amp;&amp;amp; !b.Deleted);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Evidemment il est possible de créer un filtre sur l'entité dans la méthode OnModelCreating de votre DbContext :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity&amp;lt;Blog&amp;gt;().HasQueryFilter(p =&amp;gt; !p.Deleted);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C'est une solution viable mais étant donné que je devais l'appliquer à l'ensemble de mes entités, je ne souhaitais pas configurer chaque entité de cette façon. Comment faire alors pour appliquer ce filtre une seule fois et pour l'ensemble des requêtes qui manipulent des entités de mon modèle ? &lt;br&gt;
La réponse est via la réflexion. Je ne vais pas rentrer dans le détail de ce mécanisme très puissant du .Net dans cet article. &lt;br&gt;
Là n'est pas le but. J'invoque donc une méthode de filtre au moment de l'exécution d'une requête sur mon modèle de données. Pour cela j'ai besoin d'une classe d'extension :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;internal static class SoftDeleteQueryExtension
{
    internal static void AddSoftDeleteQueryFilter(this IMutableEntityType entityData)
    {
        var methodToCall = typeof(SoftDeleteQueryExtension)
            .GetMethod(nameof(GetSoftDeleteFilter), BindingFlags.NonPublic | BindingFlags.Static)
            .MakeGenericMethod(entityData.ClrType);

        var filter = methodToCall.Invoke(null, new object[] { });

            entityData.SetQueryFilter((LambdaExpression)filter);
     }

     private static LambdaExpression GetSoftDeleteFilter&amp;lt;TEntity&amp;gt;() where TEntity : class, ISoftDelete
     {
         Expression&amp;lt;Func&amp;lt;TEntity, bool&amp;gt;&amp;gt; filter = x =&amp;gt; !x.Deleted;
            return filter;
     }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cette classe appliquera le filtre GetSoftDeleteFilter, chaque fois qu'une requête sera utilisée sur une entité de mon modèle.&lt;/p&gt;

&lt;p&gt;Il ne reste plus qu'à configurer mes entités pour bénéficier automatiquement de ce filtre, dans le OnModelCreating :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected override void OnModelCreating(ModelBuilder modelBuilder)
{
 foreach (var entityType in modelBuilder.Model.GetEntityTypes())
 {      
  if(typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType))
  {
   entityType.AddSoftDeleteQueryFilter();
  }
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le code boucle sur chaque entité de mon model, si celle-ci implémente l'interface ISoftDelete, on lui ajoute le filtre.&lt;/p&gt;

&lt;p&gt;Votre filtre est prêt ! L'exemple du départ devient donc :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Blog Get(Guid blogId)
{
    return _ctx.Blogs.FirstOrDefault(b =&amp;gt; b.Id.Equals(blogId));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cette requête filtre automatiquement les blogs qui sont supprimés logiquement. En effet, ma classe Blog hérite de BaseEntity qui implémente l'interface ISoftDelete et donc, par transition, Blog implémente également cette interface. Par conséquent au moment où la requête sera exécutée, le filtre sera appelé et appliqué.&lt;/p&gt;

&lt;p&gt;En bonus, si vous souhaitez récupérer dans certaines requêtes les entités supprimées logiquement, vous devez utiliser la méthode IgnoreQueryFilters() sur votre requête :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var deletedEntities= _ctx.Blogs.IgnoreQueryFilters()
    .Where(x =&amp;gt; x.Deleted)
    .ToList();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Happy Coding !&lt;/p&gt;

</description>
      <category>efcore</category>
      <category>csharp</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
