.NET

Sposób na wyrugowanie nulli z projektu

Posted on Updated on

null to zło: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare

A teraz jak sobie z tym radzę:
– nigdzie explicite nie tworzyć null
– uczynić wszystkie obiekty immutable z konstruktorem mającym Guardy przeciw null, zapobiegnie to inicjalizowania nullami gdy nic nie zostało ustawione explicite
– uważać na metody LINQ typu FirstOrDefault(), więc albo unikać “OrDefault”, czyli korzystać z First(), Single(), albo odpowiednio zająć się sytuacjami gdy jednak ten defaultowy null może być przekazany
– Guardy na input z zewnętrz do naszych serwisów, chodzi o wywołania naszego kodu przez kod kliencki (ale gdyby to miał być UI to sytuacja jest analogiczna – nie wpuszczać nulli).

Przeoczyłem coś oczywistego?
Czy może ktoś uważa, że to jest naiwnie zbyt proste i się nie uda.

Advertisements

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.

Małe rzeczy

Posted on

W string jest metoda string.Concat() i samą metodą się zna od zawsze, ale jednak nie wiedziałem że może przyjmować IEnumerable.

Dzielenie z resztą bez rzutowania na float

Posted on Updated on

Aby dzielenie odbyło się z resztą operacja musi się odbyć na typach double a nie na int, czyli żeby wynik nie był zaokrąglony do inta. W praktyce dzielenie na intach:

int result = 4/3; // result == 1
Kiedyś rzutowałem w ten sposób:
int rowsNeeded = (int)Math.Ceiling((double)Sources.Count / 2);
A od dziś dzięki Grzesiowi będę zmieniał drugą liczbę na explicit double:

int rowsNeeded = (int)Math.Ceiling(Sources.Count / 2D);