Monday, October 16, 2023

Checkpoint on My Journey

Here it is, it's been two and a half years since working full time as a developer, at a local IT subcontractor company (qubiz.com)

It is a terrific journey, because I learned a lot and also accomplished many things! Just a snapshot of my current role from my last 12 months: we are upgrading an old AngularJS frontend with the latest Angular (at the moment 16.2). I had the opportunity, during the past year, to research a lot of interesting stuff, implement some proof of concept functionality, together with setup for unit testing and component architecture.


I feel that the environment is providing some great incentives to fuel my passion and perform at my best. These motivational factors are:

- great mentor who really focuses on the essentials, and without micromanaging, guides my activities

- some really hard working and smart work colleagues, who are easy to work with

- a great customer, who is willing to explain and open to communication.

My intrinsic motivators that are driving my development journey :

- curiosity, and inner drive to discover new things;

- willing to try out different approaches, risking also to fail (trial and error);

- asking questions, from colleagues and mentor, articles, Stack overflow and even Chat GPT

The process is not easy. There are lots of walls to break and hills to climb, with frustrations on the way. But, from now and then, there is a new discovered feature and issue solved. My way to conquer this summit is a step by step approach, that implied the following activities:

- researching new things and technology

- building something starting from a simple structure to a more complex solution

- improving gradually on the existing complexity, including new services, unit tests, features, and best practices

- fixing bugs on the way, and learning the lessons

- documenting everything, via a Wiki page, to record all the best practices and saving the main examples that can also be used by other developers.

- onboarding junior colleagues to start with the frontend migration, which in itself is a great lesson for me, teaches me lots of stuff about development.

A couple of years ago, I was much more attracted to the backend type of development, but it became clear that a great challenge on the frontend, could be an excellent professional target. I feel that it was worth it, even if momentarily I am not developing for the backend. I think I found a sweet-spot, by luck, by chance or by a coincidence of events. I am happy to see that I can help others and I can contribute to the growth of the project.



Thursday, December 3, 2020

Print PDF and Export Excel from RDLC in Asp.Net Core

This blog post is providing some more insight and the source code to a youtube tutorial, that I consider being worthwile looking at more closely.

Original Youtube video that inspired me:



Prerequisites:
1. Asp.Net Core MVC basics
2. Basic RDLC knowledge

Steps To follow:
1. Create an Asp.Net Core 3.1 MVC project
2. Add following Nuget Packages
  •     AspNetCore.Reporting
  •     System.CodeDom
  •     System.Data.SqlClient
3. Modify the Program.cs Class, by making sure it has the below function as:
  public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseContentRoot(Directory.GetCurrentDirectory());
                    webBuilder.UseWebRoot("wwwroot");
                    webBuilder.UseStartup<Startup>();
                });

4. Add a new project to the existing solution, a WinForm application. Create an empty RDLC report there, and also a DataSet for the report.

If your Visual Studio does not include the extension for RDLC reports, please take a look https://youtu.be/qhcg4dy43xk

5. Move the empty report to your MVC project, into wwwroot/Reports folder, and the dataset to a ReportDataset folder in the MVC project.

6. Configure the dataset and then the RDLC report:




The above report will have a string (text parameter) "prm" in it.

7. Create the HomeController in the following way:

 public class HomeController : Controller

    {

        private readonly ILogger<HomeController> _logger;


        private readonly IWebHostEnvironment _webHostEnvironment;


        public HomeController(ILogger<HomeController> logger, IWebHostEnvironment webHostEnvironment)

        {

            _logger = logger;

            _webHostEnvironment = webHostEnvironment;

            System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

        }


        public IActionResult Index()

        {

            return View();

        }


        public IActionResult Print()

        {

            var dt = new DataTable();

            dt = GetEmployeeList();

            string mimetype = "";

            int extension = 1;

            var path = $"{this._webHostEnvironment.WebRootPath}\\Reports\\Employees.rdlc";

            Dictionary<string, string> parameters = new Dictionary<string, string>();

            parameters.Add("prm", "RDLC report (Set as parameter)");

            LocalReport lr = new LocalReport(path);

            lr.AddDataSource("dsEmployee", dt);

            var result = lr.Execute(RenderType.Pdf, extension, parameters, mimetype);

            return File(result.MainStream,"application/pdf");

        }


        public IActionResult Export()

        {

            var dt = new DataTable();

            dt = GetEmployeeList();

            string mimetype = "";

            int extension = 1;

            var path = $"{this._webHostEnvironment.WebRootPath}\\Reports\\Employees.rdlc";

            Dictionary<string, string> parameters = new Dictionary<string, string>();

            parameters.Add("prm", "RDLC report (Set as parameter)");

            LocalReport lr = new LocalReport(path);

            lr.AddDataSource("dsEmployee", dt);

            var result = lr.Execute(RenderType.Excel, extension, parameters, mimetype);

            return File(result.MainStream, "application/msexcel", "Export.xls");

        }

        private DataTable GetEmployeeList()

        {

            var dt = new DataTable();

            dt.Columns.Add("EmpId");

            dt.Columns.Add("EmpName");

            dt.Columns.Add("Department");

            dt.Columns.Add("BirthDate");

            DataRow row;


            for (int i = 1; i< 100; i++)

            {

                row = dt.NewRow();

                row["EmpId"] = i;

                row["EmpName"] = i.ToString() + " Empl";

                row["Department"] = "XXYY";

                row["BirthDate"] = DateTime.Now.AddDays(-10000);

                dt.Rows.Add(row);

            }

            return dt;

        }

       

    }

8. Then, add the view for the Home controller (Index.cshtml)

<div class="text-center">

    <h1 class="display-4">RDLC Print</h1>

    <div>

        <a href="../home/print" target="_blank" class="btn btn-primary">Print PDF</a>

        <a href="../home/export" target="_blank" class="btn btn-secondary">Export Excel</a>

    </div>

</div>


This will generate PDF and export excel, based on the dummy data we populated the report.

9. The result will be:






Sunday, November 29, 2020

Showing Wildfires on Google Maps with Blazor Server

One of my favorite web programming YT channels presented this idea during the past weeks. Traversy Media's tutorial was about showing Wildfires using React and Google Maps.

Please, have a look on his approach:


As I am checking out Blazor Server, I decided to give it a try, to reproduce something similar, using C# and Blazor Server.

The aim of this app is just to show the wildfires of the world, on a map centered on California, using the above mentioned technlogies.

Prerequisites:

- basics of Blazor server

- intermediate C#

- using free component for maps from Radzen Blazor

- using Nasa Web Api

The application is livewildfires.zoltanhalasz.net

Github Repo with code: https://github.com/zoltanhalasz/WildFiresBlazor

Steps for the application:

1. Create a Blazor Server App with Asp.Net Core 3.1

2. Install Blazor Radzen for Server: https://blazor.radzen.com/get-started

3. See the tutorial for the Httpclient in Blazor from Tim Corey: 



4. Our Api will be inserted in appsettings.json

  "NasaAPI": "https://eonet.sci.gsfc.nasa.gov/api/v2.1/categories/8"

Please visit the link to examine the structure of json data, that will be deserialized.

5. Create folder Data in the project and insert class named Event.cs

Have a look on my approach, how to deserialize the json string coming from Nasa, by preparing the corresponding structure of classes.

6. The page with the wildfires, will be using Google Maps Component, please study that also on Radzon Blazor https://blazor.radzen.com/googlemap

7. The main logic of the display is presented in the Index.razor component. I will not reproduce it here, since would be just a copy paste from my repo.

I hope this was useful and somebody learnt a bit of Blazor from this.

Tuesday, October 6, 2020

Simple Cryptocurrency App With Blazor Server Incl Excel Export

For some reason, I started to like developing with Blazor Server, mainly because the rapidity and simplicity of development. I tend to use some free component libraries to speed up development. I still want to mention that all my experience with Blazor Server is only on tutorial/experimental level.

This is just an application to learn Blazor Server, and familiarize with Cryptocurrencies, it has no other purpose.



Prerequisites for this app:

- basic Blazor Server (See my previous tutorials)

- install Radzen Components in your app see https://blazor.radzen.com/get-started

- using the C# wrapper for CoinPaprika Api (to get cryptocurrency info) https://github.com/MSiccDev/CoinpaprikaAPI

Code repository: https://github.com/zoltanhalasz/BlazorCoins

Application is live under: https://blazorcoins.zoltanhalasz.net/

The app works the following way:

1. the route /cointable will show the top 100 crypto currencies, using the API



2. clicking the detail per each currency, the route /coinhistory will show a chart and historical data for selected currency



3. the route /coingrid will show exactly the same info as 1) only in a Radzen Grid




Bonus:

The app has export excel capabilities, which can be achieved doing the following:

1. create wwwroot/js/DownloadExcel.js

function saveAsExcel(fileName, byteContent) {

    var link = document.createElement('a');

    link.download = fileName;

    console.log(fileName);

    link.href = 'data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,' + byteContent;

    document.body.appendChild(link);

    link.click();

    document.body.removeChild(link);

}

2. include the file reference at the bottom of _Host.cshtml, just below the other scripts

    <script src="/js/DownloadExcel.js"></script>

3. install EPPlus, latest, via Nuget Packages

4. in page /cointable (or where you need to export from excel), insert the markup for a button

 <button class="btn btn-primary" @onclick="GenerateExcel">Download</button>

5. include the following in the top of the page you want to use Excel Export :

@inject IJSRuntime iJSRuntime;

6. Write the handlers for GenerateExcel, in the code section of the page where you have the list that you need to export in Excel

private async Task DownloadExcel(IJSRuntime myJSRuntime)

    {

        byte[] file;


        if (coinList == null) return;


        ExcelPackage.LicenseContext = LicenseContext.NonCommercial;

        using (var package = new ExcelPackage())

        {

            var worksheet = package.Workbook.Worksheets.Add("Sheet1");

            worksheet.Cells.LoadFromCollection(coinList, true);

            package.Save();

            file = package.GetAsByteArray();

        }

        await myJSRuntime.InvokeAsync<List<CoinInfo>>("saveAsExcel", "CoinList.xlsx", Convert.ToBase64String(file));

    }


    private async Task GenerateExcel()

    {

        await DownloadExcel(iJSRuntime);

    }



Monday, October 5, 2020

Upload and Download Pdf files to/from MS SQL Database using Razor Pages

As most of my project applications are business related, and I still plan to create such applications, thought about the idea to store attached pdf invoices in a database.

Below is a simple sample application, using Asp.Net Core 3.1 and Razor Pages, for an invoice management system with pdf file upload/download.

The Github repo for the application is here.

The application is also online, can be tested out: https://uploadfile.zoltanhalasz.net/

Prerequisites:

- beginner/intermediate Razor Pages for Asp.Net Core, see study material here https://mydev-journey.blogspot.com/2019/11/razor-pages-not-for-shaving.html

- intermediate C# and SQL

- some basic Entity Framework Core.

My sources for learning were:

- an MVC version for the upload: https://tutexchange.com/how-to-upload-files-and-save-in-database-in-asp-net-core-mvc/

- the download, was discovered using various C# resources.

Firstly, we create the database named UploadFile and then a table:

CREATE TABLE [dbo].[Invoices](

[Id] [int] IDENTITY(1,1) NOT NULL,

[Number] [int] NOT NULL,

[Date] [datetime] NOT NULL,

[Value] [decimal](18, 2) NOT NULL,

[Attachment] [varbinary](max) NULL,

 CONSTRAINT [PK_Invoices] PRIMARY KEY CLUSTERED ([Id] ASCWITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

Steps for creating the application:
1. Create an Asp.Net Razor Pages project
.Net Core version 3.1

2. Scaffold the database into the models using https://marketplace.visualstudio.com/items?itemName=ErikEJ.EFCorePowerTools or simply copy my Data folder that is adding the necessary data structures 
Alternatively, you can use the more traditional caffolding method: https://www.entityframeworktutorial.net/efcore/create-model-for-existing-database-in-ef-core.aspx

Make sure that the model and dbContext  (called UploadFileContext) will be stored in Data folder.

3. Add to Startup.cs, ConfigureServices method:

 services.AddDbContext<UploadfileContext>(options =>
                  options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),           
                    sqlServerOptions => sqlServerOptions.CommandTimeout(100))
                    );

4. Create and Index page, that will show the list of invoices
- the page cshtml file will contain the listing of the invoices, together with the download, upload, and delete functionalities


- the PageModel class will contain the methods to deal with:

a. download the pdf file from the database:

public async Task<IActionResult> OnPostDownloadAsync(int? id)
{
            var myInv = await _context.Invoices.FirstOrDefaultAsync(m => m.Id == id);
            if (myInv == null)
            {
                return NotFound();
            }

            if (myInv.Attachment== null)
            {
                return Page();
            }
            else
            {
                byte[] byteArr = myInv.Attachment;
                string mimeType = "application/pdf";
                return new FileContentResult(byteArr, mimeType)
                {
                    FileDownloadName = $"Invoice {myInv.Number}.pdf"
                };
            }
}

b. delete attached file from database:

public async Task<IActionResult> OnPostDeleteAsync(int? id)
{
            var myInv = await _context.Invoices.FirstOrDefaultAsync(m => m.Id == id);
            if (myInv == null)
            {
                return NotFound();
            }

            if (myInv.Attachment == null)
            {
                return Page();
            }
            else
            {
                myInv.Attachment = null;
                _context.Update(myInv);
                await _context.SaveChangesAsync();
            }

            Invoices = await _context.Invoices.ToListAsync();
            return Page();
}

5. Add a page to create Invoice Data (this can be done via Razor Pages scaffolding),

and 

6. Create an Upload Page that will help with pdf file upload

This will have the file with markup, cshtml, containing the html tags for the form:
@page
@model UploadFile.Pages.UploadModel
@{
}

<h1>Upload Invoice</h1>


<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post" enctype="multipart/form-data">
            <div class="form-group">
                <div class="col-md-10">
                    <p>Upload file</p>
                    <input type="hidden" asp-for="@Model.ID" value="@Model.myID" />
                    <input asp-for="file" class="form-control" accept=".pdf" type="file" />
                </div>
            </div>
            <div class="form-group">
                <div class="col-md-10">
                    <input class="btn btn-success" type="submit" value="Upload" />
                </div>
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

And for the PageModel class, we will have the handler to deal with the file uploaded:

public class UploadModel : PageModel
{                  

        private readonly UploadfileContext _context;

        public UploadModel(UploadfileContext context)
        {
            
            _context = context;
        }
        public int ? myID { get; set; }

        [BindProperty]
        public IFormFile file { get; set; }

        [BindProperty]
        public int ? ID { get; set; }
        public void OnGet(int? id)
        {
            myID = id;
        }

        public async Task<IActionResult> OnPostAsync()
        {
            if (file != null)
            {
                if (file.Length > 0 && file.Length < 300000)
                {
                    var myInv = _context.Invoices.FirstOrDefault(x => x.Id == ID);

                    using (var target = new MemoryStream())
                    {
                        file.CopyTo(target);
                        myInv.Attachment = target.ToArray();
                    }

                    _context.Invoices.Update(myInv);
                    await _context.SaveChangesAsync();
                }

            }

            return RedirectToPage("./Index");
        }

}


7. The end result will be like this: