Skip to content

Latest commit

 

History

History
701 lines (556 loc) · 13.9 KB

dotnet.md

File metadata and controls

701 lines (556 loc) · 13.9 KB

.NET 🟦

Base

Objective

  • B1. A code corresponds to the Technical Task.

    All the mandatory tasks of the TT have been fulfilled.

  • B2. A project compiles.

    A project can be successfully built, and a compiler doesn't show any errors.

  • B3. No unhandled errors occur when executing code.

    All parts of the application work as expected while code is executing.

  • B4. All tests (if any) pass successfully.

    If a project has tests, all of them pass successfully.

Naming

  • B5. Pascal casing is used for naming classes, interfaces, structs, records, and files.

    public class LanguageCode
    {
    
    }
    
    public struct Money
    {
    
    }

  • B6. Pascal casing is used for naming public class members and constants.

    It's used for fields, properties, events, methods, etc.

    public class Person
    {
      private const string Country = "Ukraine";
      public string Name { get; }
      public bool IsActive;
      public int GetAge()
      {
      }
    }

  • B7. Camel casing is used for naming parameters, local variables and private fields. For private fields prefix _ is used.

    public class Person
    {
      private readonly string _name
      private bool _isActive;
      public int GetAge(int birthYear, int currentYear)
      {
        var age = currentYear - birthYear;
      }
    }

  • B8. English nouns are logically used for variable and property names.

    Abbreviations are used only as service variables (for loops, predicates, etc.): i, x, and so on.

    Bad:

    var data = new List<string> { "Joe", "Ross", "Chandler" };
    for (int index = 0; index < 3; index++)
    {
      Console.WriteLine(index);
    }

    Good:

    for (int i = 0; i < 3; i++)
    {
      Console.WriteLine(i);
    }
    
    var name = "Ross";

  • B9. Arrays use plural nouns for naming.

    var integers = new List<int> { 1, 2, 3, 4, 5, 6 };
    var people = new List<Person>();

  • B10. Boolean variable names start with a prefix and are affirmative.

    Bad:

    public class Post
    {
      private bool _deleted = false;
      public bool Active { get; set; }
    }

    Good:

    public class Post
    {
      private bool _isDeleted = false;
      public bool HasBillingAddress { get; set; }
    }

  • B11. Corresponding prefixes are used for names of interfaces and generic parameters.

    Interface names must start with a capital I and then a usual pascal casing. Generic parameter names must start with capital T (can be just one letter).

    public interface ICountryService<TCountry>
    {
      public TCountry GetCountry(int id);
    }
    
    public class EncryptedId<T>
    {
      public T Id { get; set; }
    }

  • B12. Asynchronous methods are marked with an Async suffix.

    public interface IProjectRepository
    {
      public Task<Project> GetByIdAsync(int id);
    }

  • B13. 🅰️ All the assets have corresponding suffixes (.component, .module, .pipe, etc.)

Formatting and Code Styling

  • B14. Curly braces are used everywhere and always start on a new line.

    Curly braces are used everywhere if the statement implies and even if the code block consists of just one line. Curly braces always start with a new line.

    public void Print(string mood)
    {
      if (mood == "fine")
      {
        Console.WriteLine("I'm fine");
      }
    }
    
    public class EmptyClass
    {
    }

  • B15. Access modifiers are always specified.

    Access modifiers (public, private, etc.) are always specified for classes, interfaces, members, etc.

    private readonly string _name;
    
    public void PrintName()
    {
      Console.WriteLine(_name);
    }
    
    public interface IPersonInterface
    {
    }

  • B16. There is always only one line between statements.

    Bad:

    private readonly string _name;
    
    
    public void PrintName()
    {
      Console.WriteLine(_name);
    }
    public interface IPersonInterface
    {
    }

    Good:

    private readonly string _name;
    
    public void PrintName()
    {
      Console.WriteLine(_name);
    }
    
    public interface IPersonInterface
    {
    }

  • B17. Imports (using section) are always at the top of the file.

    using statements reside at the top of the file with System imports going first, and then all the others sorted alphabetically.

  • B18. .NET predefined data types are not used (such as Int32, Boolean or String).

    Built-in primitive types are used instead: int, string, bool

  • B19. No asynchronous methods return void.

    All asynchonous methods return Task or Task<T>.

  • B20. LINQ method syntax used.

    LINQ method syntax is preferred over query syntax:

    int[] numbers = { 5, 10, 8, 3, 6, 12};
    
    //Query syntax:
    IEnumerable<int> evenIntsQuery =
      from num in numbers
      where num % 2 == 0
      orderby num
      select num;
    
    //Method syntax:
    IEnumerable<int> evenIntsMethod = numbers.Where(num => num % 2 == 0).OrderBy(n => n);

Project structure and organization

  • B21. One class/interface/struct/enum/etc. per file.

    Each class, interface, and all the other parts of the application reside in its files. Exception: nested classes are allowed if required.

  • B22. Application solution is logically structured using different projects and directories.
  • B23. There are no redundant NuGet packages in the application.

    All of the installed NuGet packages in different projects should be used.

  • B24. 🅰️ Folder structure maintained properly.

    It can be "folder-by-structure" or "flat-structure" or something else as long as it's consistent.

  • B25. 🅰️ Single responsibility rule is followed (one service or component per file).

Security and performance

  • B26. Sensitive data kept outside of code.
  • B27. using statement is used when working with objects that consume resources (or IDisposable interface implemented).
  • B28. Asynchronous execution isn't blocked by Task.Wait or Task.Result.
  • B29. LINQ calls are optimized.

    Bad:

    var childrenAmount = people.Where(x => x.Age < 18).Count();
    var isEmpty = people.Count() == 0;

    Good:

    var childrenAmount = people.Count(x => x.Age < 18);
    var isEmpty = !people.Any();

  • B30. SQL code is always generated using parameters (no interpolation).

    Bad:

    int id = 1;
    string name = "Bill";
    int age = 60;
    
    var sql = $"INSERT INTO USERS(id, name, age) VALUES({id}, {Name}, {Age})";
    connection.Execute(sql);

    Good:

    int id = 1;
    string name = "Bill";
    int age = 60;
    
    var sql = "INSERT INTO USERS(id, name, age) VALUES(@Id, @Name, @Age)";
    connection.Execute(sql, parameters: new
    {
      Id = id,
      Name = name,
      Age = age
    });

  • B31. Cross-Origin Requests (CORS) are enabled.
  • B32. 🅰️ Lazy loading is used for modules where possible.
  • B33. 🅰️ No memory leaks while using observables.

ASP.NET Core and API

  • B34. Interfaces are used instead of implementations. They are registered using dependency injection.
  • B35. API is (mostly) RESTful.

    At least:

    • POST requests only create resources
    • GET requests only return data, without changing anything
    • PUT requests only update information, etc
  • B36. Endpoints return obvious codes.

    At least:

    • 200 — OK
    • 201 — Created
    • 404 — Not found, etc.
  • B37. No business logic inside controllers.
  • B38. Controllers don't work with domain models.

    An application uses DTOs for input and output and performs mapping in the application layer to avoid working with domain objects in the controllers.

  • B39. Plural nouns are used for paths (instead of verbs).

Miscellaneous

  • B40. No "magic values" in code.

    Code doesn't use any "magic values". They are at the top of the class definition as constants or in separate files.

  • B41. The most abstract interfaces are used whenever possible (IEnumerable over ICollection, ICollection over IList, etc.)
  • B42. Null-conditional operators are used if there are possible null values.
  • 🅰️ B43. No (or extra minimum) logic in templates.
  • 🅰️ B44. No logic in main.ts file.

Advance

Formatting and Code Styling

  • A1. Properties and fields are above methods in classes.
  • A2. Public methods are above the private ones.
  • A3. LINQ chain is split into lines.

    Bad:

    var studentNames = studentList.Where(s => s.Age > 18).Select(s => s).Where(st => st.StandardID > 0).Select(s => s.StudentName);

    Good:

    var studentNames = studentList.Where(s => s.Age > 18)
                                  .Select(s => s)
                                  .Where(st => st.StandardID > 0)
                                  .Select(s => s.StudentName);

  • A4. No redundant else statements.

    Bad:

    if (name == null)
    {
      throw new Exception("Name cannot be null");
    }
    else
    {
      Console.WriteLine($"Hello, {name}")
    }

    Good:

    if (name == null)
    {
      throw new Exception("Name cannot be null");
    }
    Console.WriteLine($"Hello, {name}")

Entity Framework Core

  • A5. Entities for EF Core are configured using IEntityTypeConfiguration (not OnModelCreating) and split by files.
  • A6. Database miggrations are short and conscise.

Miscellaneous

  • A7. The last features of the language are used.

    // C# 8
    // Switch statements.
    public static Period GetCountry(PeriodUnit unit, int amount) =>
      unit switch
      {
        PeriodUnit.Days => Period.FromDays(amount),
        PeriodUnit.Months => Period.FromMonths(amount),
        PeriodUnit.Years => Period.FromYears(amount),
        _ => throw new ArgumentException("Not a valid period unit.")
      };
    
    // Using declaration
    using var file = new System.IO.StreamWriter("CountryCodes.txt");
    
    // Null-coalescing assignment
    var numbers = GetNumbers();
    numbers ??= new List<int>();
    
    // C# 9
    // Target typed new
    private Dictionary<string, object> _properties = new();
    
    // New pattern matching features
    if (name is not null) { ... }

  • A8. Global exception handling middleware exists and handles all possible application exceptions.
  • A9. API is documented.
  • A10. There is a logging middleware.

Objective

  • A11. The Technical Task is implemented in full.

    All mandatory and optional tasks of the TT have been fulfilled.