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:





Sunday, June 7, 2020

Weather App With Google Maps Demo Using Blazor Server, HttpClient and Radzen Components

My favorite teacher for C# on Youtube is Tim Corey, and his latest material concerns HttpClient, using a Weather Forecast Api.
See his material, and check the code provided, as this is a the beginning step towards our application. My application is live: https://httpclientwithmaps.azurewebsites.net
Final project is: https://github.com/zoltanhalasz/HttpClientWithMaps The way the final application works:
1. map is centered in Central Europe
2. click on the map- we will see the neighboring cities with available weather data
3. click on a red bullet - (marker) - the available weather forecast will be displayed in the table on the right.

Prerequisites:
1. Dotnet Blazor Server Side Basics - see my previous tutorial
2. Check Radzen Controls for Maps - check the source
https://blazor.radzen.com/googlemap
3. Check the Weather Api - it's the same source as mentioned by Tim
https://www.metaweather.com/api/
Steps to follow:
A. Take the sample application as starting point from Tim (it's a Blazor Server Application)
B. Add Blazor Radzen to the project, as presented below:
https://blazor.radzen.com/get-started
C. Add the following Changes, described below:

1. Add another class to Model folder to track the locations (which will be represented as red dots on the map)
    public class LocationModel
    {
        public int distance { get; set; }      
        public string title { get; set; }
        public string location_type { get; set; }
        public int woeid { get; set; }
        public string latt_long { get; set; }
        public double latt { get; set; }
        public double _long { get; set; }
    }

2. The code is commented, to understand the main functions, you can copy the WeatherData.razor page content into your project. This contains the functionality described in the beginning.

Please check the map functionality and weather api in the prerequisites to understand it.

3. The left menu is slightly simplified versus the Tim version, you can copy or leave it.

Enjoy! Let me know your comments.