Поиск  
Always will be ready notify the world about expectations as easy as possible: job change page
Jul 17

Quickly convert a console app into a single-point web app in C#

Quickly convert a console app into a single-point web app in C#
Автор:
Источник:
Просмотров:
1309

I found myself in need of quickly converting my C# console application into a web application, primarily, I was asked to expose our console app’s logic through an API with very little time to manage it.

This article will cover on how I eventually exposed our console app on port 5005 of the localhost, our internal network, for bonus, I’ll throw in the containerised Docker version of the app, too!

Let’s start with a simple console app:

mkdir ConsoleToWeb
cd ConsoleToWeb
dotnet new console
dotnet run

Create console app

The source of this app will look something like this:

using System;

namespace ConsoleToWeb
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!"); // => This needs to be converted into a web app
        }
    }
}

To convert this into a ASP.NET Web App, we need to do 3 things:

  • Convert the console project into a web project (csproj)
  • Introduce a Generic Host to host our Web App
  • Rewrite the Main method to run our WebHost

Converting a console csproj into a web csproj

Our original console csproj looks something like the following:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

</Project>

For a web project, we need to target a different SDK; Microsoft.NET.Sdk.Web

Also, the OutputType does not need to be an Exe.

Modify the csproj file to look like this:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
 
</Project>

Introduce a Generic Host to host our web app

The IHost class is a class designed for the new app hosting system of .NET. It handles core functionality like Dependency Injection, Configuration, Logging, …

We will use the HostBuilder to create a vanilla Web Host that will have 1 Run method which will call the code from our Console app.

Add the following code to your Program.cs file:

public static IHostBuilder CreateHostBuilder(string[] args) =>
  Host.CreateDefaultBuilder(args)
      .ConfigureWebHost(
          webHost => webHost
              .UseKestrel(kestrelOptions => { kestrelOptions.ListenAnyIP(5005); })
              .Configure(app => app
                  .Run(
                      async context =>
                      {
                          await context.Response.WriteAsync("Hello World!");
                      }
                  )));

Don’t forget about the necessary using statements:

using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;

Rewrite the Main method to run our WebHost

We’re almost there, we just need to tie it all up in the Main method of the Program.cs:

static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

Let’s examine the full working example:

using System;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;

namespace ConsoleToWeb
{
    class Program
    {
        static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHost(
                    webHost => webHost
                        .UseKestrel(kestrelOptions => { kestrelOptions.ListenAnyIP(5005); })
                        .Configure(app => app
                            .Run(
                                async context =>
                                {
                                    await context.Response.WriteAsync("Hello World!");
                                }
                            )));
    }
}

That is it! Now you can run the app using dotnet run and see the result in your browser at http://localhost:5005

ConsoleToWeb

Web localhost:5005

Dockerize it!

Since we’re on a roll here, let’s quickly Dockerize this app so that we can package and ship it.

Add a Dockerfile to the root of the ConsoleToWeb directory.

touch Dockerfile

The contents of the Dockerfile should be something like:

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build-env
WORKDIR /app

COPY *.csproj ./
RUN dotnet restore

COPY . ./
RUN dotnet publish -c Release -o out

FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
COPY --from=build-env /app/out .
EXPOSE 5005
ENTRYPOINT ["dotnet", "ConsoleToWeb.dll"]

Now that is done, we can build our image locally and tag it:

docker build . -t consoletoweb

docker build consoletoweb

After the Docker image is built, we can run the image in a container using docker run -p 5005:5005 consoletoweb

Run the Docker image

And the effect should be the same on http://localhost:5005

Web localhost:5005

Quickly adding services using FuncR

Still going strong now, let’s introduce a service, add some small implementation and have that service injected into our Web App using Dependency Injection!

Introduce the IFooService:

interface IFooService
{
    string Foo(int numberOfFoos);
}

Now, we could implement this service using a new class, or we could write just the code we need for string Foo(int numberOfFoos) method to work using a function with FuncR.

FuncR logo

FuncR is a small .NET standard library that enables you to register functions against interfaces in C#.

First we need to add FuncR to the project

dotnet add package FuncR

To use FuncR, add a ConfigureServices method to your Program.cs file:

 1: private static void ConfigureServices(IServiceCollection services)
 2: {
 3:     services.AddScopedFunction<IFooService>
 4:         (nameof(IFooService.Foo))
 5:         .Runs<int, string>(numberOfFoos =>
 6:         {
 7:             var foos = Enumerable.Range(1, numberOfFoos).Select(n => "Foo");
 8:             return $"{String.Join(", ", foos)}";
 9:         });
10: }

In this ConfigureServices method, you can register any service agains the DI container of ASP.NET, more concretely, the code above registers a Proxy for IFooService and hooks up the string Foo(int numberOfFoos) method to the function that is provided on lines 7 and 8, cool huh!?

Now we just need to tell the HostBuilder to use this method to configure its services and we’re ready to have that service injected:

using System;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using FuncR;
using System.Linq;

namespace ConsoleToWeb
{
    interface IFooService
    {
        string Foo(int numberOfFoos);
    }

    class Program
    {
        static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        private static void ConfigureServices(IServiceCollection services)
        {
            services.AddScopedFunction<IFooService>
                (nameof(IFooService.Foo))
                .Runs<int, string>(numberOfFoos =>
                {
                    var foos = Enumerable.Range(1, numberOfFoos).Select(n => "Foo");
                    return $"{String.Join(", ", foos)}";
                });
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHost(
                    webHost => webHost
                        .UseKestrel(kestrelOptions => { kestrelOptions.ListenAnyIP(5005); })
                        .ConfigureServices(ConfigureServices)
                        .Configure(app => app
                            .Run(
                                async context =>
                                {
                                    var numberOfFoos = 5;
                                    // Resolve IFooService here
                                    var fooService = context.RequestServices.GetRequiredService<IFooService>();
                                    await context.Response.WriteAsync(fooService.Foo(numberOfFoos));
                                }
                            )));
    }
}

If we now dotnet run and navigate to http://localhost:5005 we finally see some different output:

Web app

That’s a lot of Foo’s.

And naturally, the Docker example also still works after a rebuild and run:

docker build . --no-cache -t consoletoweb
docker run -p 5005:5005 consoletoweb

Wrapping up

In this article we saw how easy and quickly a console app can be converted into a ASP.NET web app, and we got an introduction to FuncR to speed up our service implementations. The full code, and more is available through GitHub:

Source: https://github.com/merken/ConsoleToWeb

Похожее
May 30
Author: Winds Of Change
Performance is paramount when developing web applications. A slow, unresponsive application results in poor user experience, losing users, and possibly business. For ASP.NET Core developers, there are many techniques and best practices to optimize application performance. Let’s explore some of...
Jul 30
Author: Emer Kurbegovic
Azure Cosmos DB is a globally distributed, multi-model database service provided by Microsoft Azure. It supports multiple data models, including document, key-value, graph, and column-family data models. In this article, we’ll explore how to add and retrieve data from Azure...
Aug 23, 2022
Author: Luis Rodrigues
Suppose we are building a web api that contains a route to send notification messages to other systems. For security matters, before sending any notification message, we need to provide some credentials to these systems to they accept our messages....
Jul 10, 2021
Author: Sam Walpole
I've recently gotten into using Docker in my development cycle and I'm really enjoying how much of a wonderful tool it is. One thing that always used to be a pain was setting up a development server to run SQL...
Написать сообщение
Тип
Почта
Имя
*Сообщение
RSS
Если вам понравился этот сайт и вы хотите меня поддержать, вы можете
Дюжина логических задач с собеседований
Soft skills: 18 самых важных навыков, которыми должен владеть каждый работник
Четыре типажа программистов
Performance review, ачивки и погоня за повышением грейда — что может причинить боль сотруднику IT-компании?
GraphQL решает кучу проблем — рассказываем, за что мы его любим
Разбираемся с middleware в ASP.NET Core
Как избавиться от прокрастинации до того, как она разрушит вашу карьеру
Функции и хранимые процедуры в PostgreSQL: зачем нужны и как применять в реальных примерах
Разрабы работают медленно и дорого — и люди считают нас лентяями. Просто в разработке всё сложно
Using a сustom PagedList class for Generic Pagination in .NET Core
LinkedIn: Sergey Drozdov
Boosty
Donate to support the project
GitHub account
GitHub profile