Pages

Friday, September 11, 2020

Swagger: Getting started guide - Generate web api documention with Swagger ASP.NET Core

Introduction

For people that don't know what Swagger is.

"Swagger is a set of open-source tools built around the OpenAPI Specification that can help you design, build, document and consume REST APIs."
Source

This "Swagger: Getting started guide" post will explain the basics and how to use Swagger with:
  • ASP.NET Core 3.1
  • Swashbuckle.AspNetCore 5.5.1

  • Source code

    You can find the source code of this post on: https://github.com/gergroen/Swagger-getting-started-guide

    Post summary

  • Installing NuGet
  • Create dto
  • Create api controller
  • Configure services and application
  • Swagger page

  • Installing NuGet

    First create a empty ASP.NET Core Web application project and install the next nuget packages.
  • Swashbuckle.AspNetCore

  • Create dto

    Create a dto class like this CoffeeDto class.
    namespace Swagger.GettingStarted
    {
        public class CoffeeDto
        {
            public string Name { get; set; } = string.Empty;
        }
    }
    

    Create api controller

    Add a api controller class for your dto. And add comments to the operations.
    using System.Collections.Generic;
    using Microsoft.AspNetCore.Mvc;
    
    namespace Swagger.GettingStarted
    {
        /// <summary>
        /// Coffee controller
        /// </summary>
        [Produces("application/json")]
        [Route("api/[controller]")]
        [ApiController]
        public class CoffeeController : ControllerBase
        {
            /// <summary>
            /// Get all coffees
            /// </summary>
            /// <returns></returns>
            [Route("")]
            [HttpGet]
            public List GetAll()
            {
                return new List
                {
                    new CoffeeDto{ Name = "Americano"},
                    new CoffeeDto{ Name = "Latte"},
                    new CoffeeDto{ Name = "Cappuccino"},
                    new CoffeeDto{ Name = "Espresso"},
                };
            }
        }
    }
    

    Configure services and application

    Enable "xml documentation file" in the project -> properties -> build -> output.
    Then we need to configure in the Startup class the services with AddSwaggerGen where we include the xml documentation file.
    Configure the application with UseSwagger to generate the openapi json and UseSwaggerUI for the swagger documentation page.
    using System;
    using System.IO;
    using System.Reflection;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace Swagger.GettingStarted
    {
        public class Startup
        {
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllers();
                services.AddSwaggerGen(c => {
                    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                    c.IncludeXmlComments(xmlPath);
                });
            }
    
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                app.UseSwagger();
                app.UseSwaggerUI(c =>
                {
                    c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
                });
                app.UseRouting();
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
        }
    }
    

    Swagger page

    Run the application and add /swagger to the url in the browser to show the swagger page.
    On this page is shown all the controllers, the operations and the dto's of the api.

    Useful links:

    Source code
    Swagger documentation

    Thursday, September 10, 2020

    Topshelf: Getting started guide - Run .net core console application as windows service

    Introduction

    For people that don't know what Topshelf is.

    "Topshelf is a framework for hosting services written using the .NET framework.
    The creation of services is simplified, allowing developers to create a simple console application that can be installed as a service using Topshelf.
    The reason for this is simple: It is far easier to debug a console application than a service.
    And once the application is tested and ready for production, Topshelf makes it easy to install the application as a service."

    Source

    This "Topshelf: Getting started guide" post will explain the basics and how to use Topshelf with:
  • .NET Core 3.1
  • Topshelf 4.2.1
  • Serilog 2.9.0

  • Source code

    You can find the source code of this post on: https://github.com/gergroen/Topshelf-getting-started-guide

    Post summary

  • Installing NuGet
  • Create service class
  • Configure logging and windows service
  • Control the windows service
  • The result

  • Installing NuGet

    First create a .Net core console project and install the next nuget packages.
  • Serilog
  • Serilog.Sinks.Console
  • Serilog.Sinks.File
  • Topshelf
  • Topshelf.Serilog

  • Create service class

    Create a Service class. In this example the implementation has only log lines
    but here you can add your own functionality for the windows service.
    using Serilog;
    
    namespace Topshelf.GettingStarted
    {
        public class Service
        {
            public void Start()
            {
                Log.Information("Started");
            }
    
            public void Stop()
            {
                Log.Information("Stopped");
            }
        }
    }
    

    Configure logging and windows service

    Logging and the windows service are configured in the Program.Main.
    using System;
    using Serilog;
    
    namespace Topshelf.GettingStarted
    {
        class Program
        {
            static void Main(string[] args)
            {
                Log.Logger = new LoggerConfiguration()
                    .WriteTo.Console()
                    .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
                    .CreateLogger();
    
                var rc = HostFactory.Run(x =>
                {
                    x.Service(s =>
                    {
                        s.ConstructUsing(name => new Service());
                        s.WhenStarted(tc => tc.Start());
                        s.WhenStopped(tc => tc.Stop());
                    });
                    x.RunAsLocalSystem();
    
                    x.SetDescription("Topshelf Getting Started");
                    x.SetDisplayName("Topshelf.GettingStarted");
                    x.SetServiceName("Topshelf.GettingStarted");
                    x.UseSerilog();
                });
    
                var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode());
                Environment.ExitCode = exitCode;
            }
        }
    }
    

    This is the result when we run the console application.

    Control the windows service

    To control the windows service we create the next batch files.

    _install-service.bat
    "%~dp0Topshelf.GettingStarted" install
    _start-service.bat
    "%~dp0Topshelf.GettingStarted" start
    _stop-service.bat
    "%~dp0Topshelf.GettingStarted" stop
    _uninstall-service.bat
    "%~dp0Topshelf.GettingStarted" uninstall
    Set the "Copy to output directory" of the batch files to "Copy if newer".

    The result

    Now you can run _install-service.bat as Administrator to install the application as windows service.
    If you open the Services console you will see the application in the list.

    Useful links:

    Source code
    Topshelf documentation

    Saturday, September 5, 2020

    NHibernate: Getting started guide - Object-relational mapping for .net core

    Introduction

    For people that don't know what NHibernate is.

    "NHibernate is a mature, open source object-relational mapper for the .NET framework.
    It's actively developed, fully featured and used in thousands of successful projects."
    Source

    This "NHibernate: Getting started guide" post will explain the basics and how to use NHibernate with:
  • .NET Core 3.1
  • NHibernate 5.3.2
  • NUnit 3.12.0
  • SQLite 1.0.113.1
  • NuGet

  • Used techniques and design patterns

  • NHibernate Mapping by code
  • Repository
  • Domain Model
  • Unit testing

  • Source code

    You can find the source code of this post on: https://github.com/gergroen/NHibernate-getting-started-guide

    Post summary

  • Installing NuGet
  • Download NHibernate, SQLite and NUnit with NuGet
  • Create and test a domain model
  • Create and test NHibernate mapping by code
  • Configure NHibernate for SQLite
  • Create and test a NHibernate helper
  • Create and test a repository

  • Installing NuGet

    First install the NuGet Package Manager if it is not installed.

    Download NHibernate, SQLite and NUnit with NuGet

    NHibernate
    Now you can open the "NuGet Package Manager" by clicking with the right mouse button on your project and choose "Manage NuGet Packges..."

    Search for "NHibernate" and click on Install.

    Two references are added to your project "NHibernate" and "Iesi.Collections".
    "Iesi.Collections" is added because "NHibernate" has a dependency on "Iesi.Collections".

    SQLite
    Start the "NuGet Package Manager" again, search for "System.Data.SQLite.Core" and click on Install.

    NUnit
    Start the "NuGet Package Manager" again, search for "NUnit" and click on install.
    To run the unit tests in Visual Studio you can use tools like ReSharper or Testdriven.net.
    If you don't need to run the unit tests in Visual Studio you can download the "NUnit.Runners" via NuGet.
    "NUnit.Runners" allows you to run the unit tests through the command line or a gui.

    Create and test a domain model

    After downloading all the packages we can start with the domain model.
    First we create a simple "Person" domain model with the properties "Id", "FirstName" and a "LastName".
    Also we include the method "GetFullName();".
    using System;
    
    namespace NHibernate.GettingStarted.Model
    {
        public class Person
        {
            public virtual Guid Id { get; set; }
            public virtual string FirstName { get; set; }
            public virtual string LastName { get; set; }
    
            public virtual string GetFullName()
            {
               return string.Format("{0} {1}", FirstName, LastName);
            }
        }
    }
    
    After creating the domain model we create a test class to test the domain model.
    We're using "NUnit" to unit test our domain model.
    using NHibernate.GettingStarted.Model;
    using NUnit.Framework;
    
    namespace NHibernate.GettingStarted.Test
    {
        [TestFixture]
        public class PersonTest
        {
            [Test]
            public void GetFullNameTest()
            {
                var person = new Person
                                 {
                                     FirstName = "Test",
                                     LastName = "Kees"
                                 };
    
                Assert.AreEqual("Test", person.FirstName);
                Assert.AreEqual("Kees", person.LastName);
    
                Assert.AreEqual("Test Kees", person.GetFullName());
            }
        }
    }
    
    

    Create and test NHibernate mapping by code

    You can create the NHibernate mapping for your domain models in different ways. In this example i use NHibernate mapping by code.
    using NHibernate.GettingStarted.Model;
    using NHibernate.Mapping.ByCode;
    using NHibernate.Mapping.ByCode.Conformist;
    
    namespace NHibernate.GettingStarted.Dao
    {
        public class PersonMap : ClassMapping<Person>
        {
            public PersonMap()
            {
                Id(x => x.Id, m => m.Generator(Generators.GuidComb));
                Property(x => x.FirstName);
                Property(x => x.LastName);
            }
        }
    }
    
    To see the result of the mapping we create this unit test.
    The test output is the traditional xml NHibernate mapping.
    using System;
    using System.Xml.Serialization;
    using NHibernate.GettingStarted.Dao;
    using NHibernate.Mapping.ByCode;
    using NUnit.Framework;
    
    namespace NHibernate.GettingStarted.Test
    {
        [TestFixture]
        public class PersonMapTest
        {
            [Test]
            public void CanGenerateXmlMapping()
            {
                var mapper = new ModelMapper();
                mapper.AddMapping<PersonMap>();
    
                var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
                var xmlSerializer = new XmlSerializer(mapping.GetType());
     
                xmlSerializer.Serialize(Console.Out, mapping);
            }
        }
    }
    

    Test output:
    <?xml version="1.0" encoding="Windows-1252"?> 
    <hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="NHibernate.GettingStarted.Model" assembly="NHibernate.GettingStarted" xmlns="urn:nhibernate-mapping-2.2">
      <class name="Person">
        <id name="Id" type="Guid">
          <generator class="guid.comb" />
        </id>
        <property name="FirstName" />
        <property name="LastName" />
      </class>
    </hibernate-mapping>
    

    Configure NHibernate for SQLite

    To configure NHibernate we need to create a "nhibernate.cfg.xml" file and set the Copy to Output Directory to "Copy Always".
    <?xml version="1.0" encoding="UTF-8"?>
    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
      <session-factory name="NHibernate.Test">
        <property name="connection.driver_class">NHibernate.Driver.SQLite20Driver</property>
        <property name="connection.connection_string">Data Source=test.db;Version=3;New=True</property>
        <property name="dialect">NHibernate.Dialect.SQLiteDialect</property>
        <property name="show_sql">true</property>
      </session-factory>
    </hibernate-configuration>
    

    Create and test a NHibernate helper

    Now we create a NHibernate helper class to load the configuration and the mapping to create sessions.
    using System.Collections.Generic;
    using NHibernate.Cfg;
    using NHibernate.Cfg.MappingSchema;
    using NHibernate.Mapping.ByCode;
    
    namespace NHibernate.GettingStarted.Dao
    {
        public static class NHibernateHelper
        {
            private static ISessionFactory _sessionFactory;
            private static Configuration _configuration;
            private static HbmMapping _mapping;
    
            public static ISession OpenSession()
            {
                //Open and return the nhibernate session
                return SessionFactory.OpenSession();
            }
    
            public static ISessionFactory SessionFactory
            {
                get
                {
                    if (_sessionFactory == null)
                    {
                        //Create the session factory
                        _sessionFactory = Configuration.BuildSessionFactory();
                    }
                    return _sessionFactory;
                }
            }
    
            public static Configuration Configuration
            {
                get
                {
                    if (_configuration == null)
                    {
                        //Create the nhibernate configuration
                        _configuration = CreateConfiguration();
                    }
                    return _configuration;
                }
            }
    
            public static HbmMapping Mapping
            {
                get
                {
                    if (_mapping == null)
                    {
                        //Create the mapping
                        _mapping = CreateMapping();
                    }
                    return _mapping;
                }
            }
    
            private static Configuration CreateConfiguration()
            {
                var configuration = new Configuration();
                //Loads properties from hibernate.cfg.xml
                configuration.Configure();
                //Loads nhibernate mappings 
                configuration.AddDeserializedMapping(Mapping, null);
    
                return configuration;
            }
    
            private static HbmMapping CreateMapping()
            {
                var mapper = new ModelMapper();
                //Add the person mapping to the model mapper
                mapper.AddMappings(new List<System.Type> { typeof(PersonMap) });
                //Create and return a HbmMapping of the model mapping in code
                return mapper.CompileMappingForAllExplicitlyAddedEntities();
            }
        }
    }

    Database schema test
    using System;
    using NHibernate.Tool.hbm2ddl;
    using NUnit.Framework;
    
    namespace NHibernate.GettingStarted.Dao.Test
    {
        [TestFixture]
        public class SchemaTest
        {
            [Test]
            public void CanGenerateSchema()
            {
                var schemaUpdate = new SchemaUpdate(NHibernateHelper.Configuration);
                schemaUpdate.Execute(Console.WriteLine, true);
            }
        }
    }
    
    Test output:
    create table Person (
      Id UNIQUEIDENTIFIER not null,
      FirstName TEXT,
      LastName TEXT,
      primary key (Id)
    )
    

    Create and test a Repository

    With the NHibernateHelper we can create a person repository to save, get, update and delete persons to the SQLite database.
    First we create a repository interface.
    using System;
    
    namespace NHibernate.GettingStarted.Model
    {
        public interface IPersonRepository
        {
            /// <summary>
            /// Get person entity by id
            /// </summary>
            /// <param name="id">id</param>
            /// <returns>person</returns>
            Person Get(Guid id);
    
            /// <summary>
            /// Save person entity
            /// </summary>
            /// <param name="person">person</param>
            void Save(Person person);
            
            /// <summary>
            /// Update person entity
            /// </summary>
            /// <param name="person">person</param>
            void Update(Person person);
    
            /// <summary>
            /// Delete person entity
            /// </summary>
            /// <param name="person">person</param>
            void Delete(Person person);
    
            /// <summary>
            /// Row count person in db
            /// </summary>
            /// <returns>number of rows</returns>
            long RowCount();
        }
    }
    
    Repository class
    using System;
    using NHibernate.GettingStarted.Model;
    
    namespace NHibernate.GettingStarted.Dao
    {
        public class NHibernatePersonRepository : IPersonRepository
        {
            public void Save(Person person)
            {
                using (ISession session = NHibernateHelper.OpenSession())
                using (ITransaction transaction = session.BeginTransaction())
                {
                    session.Save(person);
                    transaction.Commit();
                }
            }
    
            public Person Get(Guid id)
            {
                using (ISession session = NHibernateHelper.OpenSession())
                    return session.Get<Person>(id);
            }
    
            public void Update(Person person)
            {
                using (ISession session = NHibernateHelper.OpenSession())
                using (ITransaction transaction = session.BeginTransaction())
                {
                    session.Update(person);
                    transaction.Commit();
                }
            }
    
            public void Delete(Person person)
            {
                using (ISession session = NHibernateHelper.OpenSession())
                using (ITransaction transaction = session.BeginTransaction())
                {
                    session.Delete(person);
                    transaction.Commit();
                }
            }
    
            public long RowCount()
            {
                using (ISession session = NHibernateHelper.OpenSession())
                {
                    return session.QueryOver<Person>().RowCountInt64();
                }
            }
        }
    }
    
    Repository test
    using System.IO;
    using NHibernate.GettingStarted.Model;
    using NHibernate.Tool.hbm2ddl;
    using NUnit.Framework;
    
    namespace NHibernate.GettingStarted.Dao.Test
    {
        [TestFixture]
        public class NHibernatePersonRepositoryTest
        {
            private IPersonRepository _personRepo;
    
            [SetUp]
            public void CreateSchema()
            {
                DeleteDatabaseIfExists();
    
                var schemaUpdate = new SchemaUpdate(NHibernateHelper.Configuration);
                schemaUpdate.Execute(false, true);
    
                _personRepo = new NHibernatePersonRepository();
            }
    
            [Test]
            public void CanSavePerson()
            {
                _personRepo.Save(new Person());
                Assert.AreEqual(1, _personRepo.RowCount());
            }
    
            [Test]
            public void CanGetPerson()
            {
                var person = new Person();
                _personRepo.Save(person);
                Assert.AreEqual(1, _personRepo.RowCount());
    
                person = _personRepo.Get(person.Id);
                Assert.IsNotNull(person);
            }
    
            [Test]
            public void CanUpdatePerson()
            {
                var person = new Person();
                _personRepo.Save(person);
                Assert.AreEqual(1, _personRepo.RowCount());
    
                person = _personRepo.Get(person.Id);
                person.FirstName = "Test";
                _personRepo.Update(person);
    
                Assert.AreEqual(1, _personRepo.RowCount());
                Assert.AreEqual("Test", _personRepo.Get(person.Id).FirstName);
            }
    
            [Test]
            public void CanDeletePerson()
            {
                var person = new Person();
                _personRepo.Save(person);
                Assert.AreEqual(1, _personRepo.RowCount());
    
                _personRepo.Delete(person);
                Assert.AreEqual(0, _personRepo.RowCount());
            }
    
            [TearDown]
            public void DeleteDatabaseIfExists()
            {
                if (File.Exists("test.db"))
                    File.Delete("test.db");
            }
        }
    }
    

    Useful links:

    Source code
    NHibernate documentation 

    Wednesday, September 2, 2020

    Developer blog: Getting started guide - Stackoverflow code styling for blog posts

    Stackoverflow.com use google-code-prettify as syntax highlighter.
    To use google-code-prettify on Blogger.com you have to add the following css to your template:
    /* Pretty printing styles. Used with prettify.js. */
    
    /* SPAN elements with the classes below are added by prettyprint. */
    .pln { color: #000 }  /* plain text */
     
    @media screen {
      .str { color: #800000 }  /* string content */
      .kwd { color: #00008b }  /* a keyword */
      .com { color: #808080 }  /* a comment */
      .typ { color: #2b91af }  /* a type name */
      .lit { color: #800000 }  /* a literal value */
      /* punctuation, lisp open bracket, lisp close bracket */
      .pun, .opn, .clo { color: #000 }
      .tag { color: #800000 }  /* a markup tag name */
      .atn { color: #ff0000 }  /* a markup attribute name */
      .atv { color: #0000ff }  /* a markup attribute value */
      .dec, .var { color: #000 }  /* a declaration; a variable name */
      .fun { color: #000 }  /* a function name */
    }
    
    /* Use higher contrast and text-weight for printable form. */
    @media print, projection {
      .str { color: #060 }
      .kwd { color: #006; font-weight: bold }
      .com { color: #600; font-style: italic }
      .typ { color: #404; font-weight: bold }
      .lit { color: #044 }
      .pun, .opn, .clo { color: #440 }
      .tag { color: #006; font-weight: bold }
      .atn { color: #404 }
      .atv { color: #060 }
    }
    
    /* Put a border around prettyprinted code snippets. */
    pre.prettyprint 
    { 
      background-color:#eeeeee;
      font-family:Consolas; 
      font-size:10.5pt; 
      overflow:auto;
      padding:5px;
    }
    
    /* Specify class=linenums on a pre to get line numbering */
    ol.linenums { margin-top: 0; margin-bottom: 0 } /* IE indents via margin-left */
    li.L0,
    li.L1,
    li.L2,
    li.L3,
    li.L5,
    li.L6,
    li.L7,
    li.L8 { list-style-type: none }
    /* Alternate shading for lines */
    li.L1,
    li.L3,
    li.L5,
    li.L7,
    li.L9 { background: #eee }
    
    
    Also you have to add the following javascript between the <head></head> tags of your blog.
      <script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>
    
    And add onload='prettyPrint()' in the body tag.
    <body onload='prettyPrint()'>
    
    Now you can create posts with code snippets stackoverflow.com style.
    You need to add your code between <pre class="prettyprint"> and </pre>.
    <pre class="prettyprint lang-cs">
    
    // A Hello World! program in C#.
    using System;
    namespace HelloWorld
    {
        class Hello 
        {
            static void Main() 
            {
                Console.WriteLine("Hello World!");
    
                // Keep the console window open in debug mode.
                Console.WriteLine("Press any key to exit.");
                Console.ReadKey();
            }
        }
    }
    
    </pre>
    
    The result of the code snippet is:
    // A Hello World! program in C#.
    using System;
    namespace HelloWorld
    {
        class Hello 
        {
            static void Main() 
            {
                Console.WriteLine("Hello World!");
    
                // Keep the console window open in debug mode.
                Console.WriteLine("Press any key to exit.");
                Console.ReadKey();
            }
        }
    }