Programowanie

Biblioteka Refit lepsza od RestSharp

Posted on Updated on

Po lekturze Exploring refit, an automatic type-safe REST library for .NET Standard i zamienieniu bibliotek w jednym projekcie mogę stwierdzić że Refit jest po prostu lepszy.

Tak wygląda korzystanie z niego:

public interface IGitHubApi
{
    [Get("/users/{user}")]
    Task<User> GetUser(string user);
}


var gitHubApi = RestService.For<IGitHubApi>("https://api.github.com");

var octocat = await gitHubApi.GetUser("octocat");

RestSharp problem z deserializacją

Przy okazji jeśli wywala wam się serializacja w RestSharp’ie to może powodem jest brak domyślnego Json.NET – Newtonsoft serializera. Opis jak sobie z tym poradzić

Zapętlone projekty? – rozwiązaniem interfejs

Posted on

Mam projekt Persistence, który odpowiada za połączenie interfejsów repozytoriów z EntityFrameworkiem (Architektura heksagonalna). Dużą częścią tego projektu są SampleData. Są to dane, którymi seedujemy baze za starcie, aby można było wyklikać skomplikowane scenariusze na podczas Demo, oraz aby na nich można było odpalać UI testy w Selenium. (aplikacja pobiera dane z innych źródeł więc niemożliwe jest wypełnienie danych poprzez wyklikanie z przeglądarki).

Tak wyglądał kod, gdy wszystko było w jednym projekcie:

class MyContext : DbContext
{
    public DbSet<User> Users { get; set; }
}

class MigrationsConfiguration : DbMigrationsConfiguration<MyContext>
{
    protected override void Seed(MyContext context)
    {
        SeedConfiguration.Seed(context);
    }
}

class SeedConfiguration
{
    public static void Seed(MyContext context)
    {
        var user = new User
        {
            Id = 1,
            UserName = "John Galt"
        };

        context.Users.AddOrUpdate(x => x.Id, user);
    }
}

Ten SampleData był już osobnym katalogiem i zaczał z czasem bardzo rosnąć. Stało się jasne, że Persistence sam w sobie bardzo rzadko się zmienia (czasem jakieś migracje, nowe pola), czyli jest “stabilny”. Natomiast SampleData było żywe (nowe rzeczy, refactoringi itp).

Jest to więc znakomity kandydat do wydzielenia nowego projetku. I tutaj pojawił się problem. Persistence potrzebowało mieć referencję do SampleData, aby wywołać explicite metodę Seed(), a SampleData potrzebowało referencję do Persistence aby mieć DbContext na którym operuje.

Tego problemu nie zauważyłem od razu, dopiero na końcu wydzielania nowego projektu uderzyło mnie to. Cięzko było to obejść :/ Próbowałem coś w stylu ładowanie dynamiczne dll’ek z katalogu i skanowanie refleksja kto implementuję metodę którą chcemy wywołać – wszystko HACKy. Nie warto robić hacków więc zostawiłem temat. Po miesiącu rozwiązanie samo mnie uderzyło gdy wcale o nim nie myślałem 🙂 i był to nowy interfejs.

Kod gdy mamy dwa projekty i Persistence ma referencję do SampleData

// SampleData project
public interface ISampleDataContext
{
    DbSet<User> Users { get; set; }
}

class SeedConfiguration
{
    public static void Seed(ISampleDataContext context)
    {
        var user = new User
        {
            Id = 1,
            UserName = "John Galt"
        };

        context.Users.AddOrUpdate(x => x.Id, user);
    }
}

// Persistence project
class MyContext : DbContext, ISampleDataContext
{
    public DbSet<User> Users { get; set; }
}

class MigrationsConfiguration : DbMigrationsConfiguration<MyContext>
{
    protected override void Seed(MyContext context)
    {
        // works cause MyContext implements ISampleDataContext
        SeedConfiguration.Seed(context);
    }
}

Czyli eleganckie rozwiązanie zgodne z OOP.

Od siebie mogę dodać, że nawet jeśli wydzieleniu tego projetku było ważną rzecza (nie tylko ja widziałem tą potrzebę) to nie warto było robić z tego powodu dirty HACKA. Czasem lepiej poczekać, pogadać z różnymi ludżmi… Czasem ktoś nam poda dobre rozwiązanie a czasem ono samo do nas przyjdzie.

Niepisane (już spisane) zasady w projekcie C#

Posted on Updated on

Chciałem po prostu spisać dla potomnych rzeczy które dotychczas może nie były spisane.
Będą dopisywane nowe.

  • nazywanie zmiennej “_” oznacza, że ta zmienna jest nieistotna i pomijamy ją w dalszym kodzie, mimo że musimy jej użyć. Najczęściej pojawia się w lambdach, ale jest też kilka przykładów w pozostałym kodzie, np:

    static readonly ConcurrentDictionary<string, UserModel> LoggedInUsers;
    
    public static void Unsubscribe()
    {
        UserModel _;
        LoggedInUsers.TryRemove(Login, out _);
    }
    

* Przeczytana i zrozumiana książka Clean Code, Czysty kod
* R# nie sugeruje zamieniania na var.
* nie używamy ref i out.
* brak zakomentowanego kodu, jeśli coś kiedyś byśmy potrzebowal, to do tego służy system kontroli wersji (git)
* korzystanie z tooli (R#, StyleCop etc), jeśli jakaś reguła może być automatyczna to ludzie nie powinni tracić czasu na ręczne (mentalne) Code Review.

Nazywanie nieużywanych parametrów _ a StyleCop

Posted on Updated on

Gdy użyjemy _ jako nazwy parametru w metodzie to dostaniemy warning SA1313 (The name of a parameter in C# does not begin with a lower-case letter.). Zdarza mi się taki kod gdy implementuje abstrakcyjną metodę która akurat w danej klasie nie używa jakiegoś parametru. I niestety StyleCop krzyczy.

Rozwiązałem to w ten sposób że ignoruję globalnie tą regułę. Jeśli gdzieś indziej w kodzie ktoś nazwie parametr _table_length to Resharper mi wyłapie ten błąd i zasugeruje poprawę.

VsVim – małe info

Posted on Updated on

Gdy źle ustawiłem jak rozwiązać konfliktujące skróty klawiaturowe:

vsvim bindings conflicts

Btw VsVim działa z Visual Studio 2017. https://marketplace.visualstudio.com/items?itemName=JaredParMSFT.VsVim

[StyleCop] Reguły które wyłączam

Posted on Updated on

… kiedyś opiszę te podstawowe …

A teraz te, z którymi na bieżąco trzeba sobie radzić.

* EF add-migration dodaje trailing whitespace (SA1028) w klasach z kodem migracji. – rozwiazanie niżej w treści posta (Exclude tylko dla danego namespace)

Można wyłączyć reguły per projekt (namespace, class, etc)

Gdy StyleCop zaoferuje nam fixa to możemy dać “Suppress in suppression file” i wtedy zostanie utworzone coś takiego:

// GlobalSuppressions.cs

// This file is used by Code Analysis to maintain SuppressMessage 
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given 
// a specific target and scoped to a namespace, type, member, etc.

using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1618:Generic type parameters must be documented", Justification = "<Pending>", Scope = "member", Target = "~M:YourOrganization.Project.Class``2(AutoMapper.IMappingExpression{``0,``1},System.Linq.Expressions.Expression{System.Func{``1,System.Object}})~AutoMapper.IMappingExpression{``0,``1}")]

Dużo przewijania w prawo.

Exclude tylko dla danego namespace

GlobalSuppressions.cs można wrzucić dowolny katalog i wtedy będzie dotyczyło tylko tych z danego i poniżej. Zakładam że namespace’y odpowiadają katalogom.

StyleCop & operator precedence in C#

Posted on Updated on

Przekład niedobrych praktyk które wyłapie StyleCop:

stylecop-conditional-expression-must-declare-precedence

O operator precedence i czy nawiasy są istotne możecie poczytać na:

http://softwareengineering.stackexchange.com/questions/201175/should-i-use-parentheses-in-logical-statements-even-where-not-necessary
– pierwsza i druga odpowiedź warta przeczytania.