How to improve Linq-to-sql in MVC action method

193 Views Asked by At

I'm working on a method to generate pdf. For this purpose I'm using tuespechkin library . Conversion itself is quite fast. Most of the time it takes retrieving data from the database and creating html representation of pdf. To improve the performance I tried to use async methods and PLinq but it is not much help. Is there a way to optimize this method?

most expensive part of the code(Updated):

StringBuilder html = new StringBuilder(1024 * 1024); // adapt memory reservation depending on document size
//---------------------------- Start page -----------------------------------------------------------//
imageArray = System.IO.File.ReadAllBytes(HttpContext.Server.MapPath("../Content/images/pdf_logo.jpg"));
base64ImageRepresentation = Convert.ToBase64String(imageArray);
html.Append("<div class='break' style='margin: 0 0 0 0px;'>");
   html.Append("<img class='report-img' style='margin:150px 0 400px 0;' src='data:image/jpeg;base64," + base64ImageRepresentation + "'></img>");
   html.Append("<p class='purpleText' style='color:#8E0033; font-size: 30px; margin:0; line-height:20px;'>" + title + "</p>" +
            "<p class='purpleText' style='color:#8E0033; font-size: 16px;'>" + bigTitle + "</p>");
   html.Append("<p style='color:#980000; font-size: 50px; margin: 46px 0 0 0;'>" + custName.firstName + " <span>" + custName.lastName + "</span></p>");
html.Append("</div>");

//------------------------------------ 2nd page -----------------------------------------------------------//
html.Append("<div class='text-block break'></div>");

//=======================================================================================================================//
//------------------------------------- Generate menu (p4) ----------------------------------------------------------//
html.Append(header);
html.Append("<span  class='menu-title'>INNHOLDSFORTEGNELSE</span>");
html.Append("<ul class='content-list break'>");

//------------------ Innlednings menu --------------------------//
if (await db.Innlednings.AnyAsync(n => n.CustomerId == customerId))
{
   html.Append("<li class='list-header'>Innledning</li>");
}
//------------------ Fordelings menu --------------------------//
if (await db.Fordelings.AnyAsync(n => n.CustomerId == customerId))
{
   html.Append("<li class='list-header'>Fordeling av bruk og regninger</li>");
}
//--------------------------------- Report Area Bruk Menu ----------------------------------------------------//
//--------------------------- Get transaction category id of selected area ---------------------------//
const string tranCatBruk = "Bruk";
var tranCatIdBruk = 0;
if (await db.TransactionCategories.AnyAsync(n => n.Name == tranCatBruk))
{
    tranCatIdBruk = (await (db.TransactionCategories.FirstAsync(n => n.Name == tranCatBruk))).Id;
}
else
{
    throw new HttpException(404, "'" + tranCatBruk + "' transaction category does not exist!");
}
if (await db.ReportViews.AnyAsync(n => n.CustomerId == customerId && n.TransactionCategoryId == tranCatIdBruk))
{
   html.Append("<li class='list-header'>Brukskonto</li>");

    if (await db.FordelingBruks.AnyAsync(n => n.CustomerId == customerId))
    {
       html.Append("<li class='list-level2'>Fordeling av brukskonto</li>");
    }

    var rvBrukItems = await
        db.ReportViews.Where(n => n.CustomerId == customerId && n.TransactionCategoryId == tranCatIdBruk).ToListAsync();
    foreach (var rvItem in rvBrukItems)
    {
        var mainCatName = (await db.Categories.FirstAsync(n => n.Id == rvItem.MainCategoryId)).Name;

        if (!rvItem.TotalPerMonthImage.IsNullOrWhiteSpace() ||
            !rvItem.DifferentStoresImage.IsNullOrWhiteSpace() ||
            !rvItem.SubCategoryImage.IsNullOrWhiteSpace() ||
            !rvItem.TransactionsPerMonthImage.IsNullOrWhiteSpace() ||
            !rvItem.AverageTradeValueImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level2'>" + mainCatName + "</li>");
        }
        //----------------- get images --------------------------//
        if (!rvItem.TotalPerMonthImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level3'>Totalt pr. måned</li>");
        }
        if (!rvItem.DifferentStoresImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level3'>Fordeling</li>");
        }
        if (!rvItem.SubCategoryImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level3'>Kategorier</li>");
        }
        if (!rvItem.TransactionsPerMonthImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level3'>Transaksjoner pr. måned</li>");

        }
        if (!rvItem.AverageTradeValueImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level3'>Gjennomsnittlig handlebeløp</li>");

        }

    }

}
//--------------------------------- Report Area Regning Menu ----------------------------------------------------//
//--------------------------- Get transaction category id of selected area ---------------------------//
const string tranCatRegning = "Regning";
var tranCatIdRegning = 0;
if (await db.TransactionCategories.AnyAsync(n => n.Name == tranCatRegning))
{
    tranCatIdRegning = (await db.TransactionCategories.FirstAsync(n => n.Name == tranCatRegning)).Id;

}
else
{
    throw new HttpException(404, "'" + tranCatRegning + "' transaction category does not exist!");
}
if (await db.ReportViews.AnyAsync(n => n.CustomerId == customerId && n.TransactionCategoryId == tranCatIdRegning))
{
   html.Append("<li class='list-header'>Regningskonto</li>");

    if (await db.FordelingBruks.AnyAsync(n => n.CustomerId == customerId))
    {
       html.Append("<li class='list-level2'>Fordeling av regningskonto</li>");
    }

    var rvBrukItems = await 
        db.ReportViews.Where(n => n.CustomerId == customerId && n.TransactionCategoryId == tranCatIdRegning).ToListAsync();

    foreach (var rvItem in rvBrukItems)
    {
        var mainCatName = (await db.Categories.FirstAsync(n => n.Id == rvItem.MainCategoryId)).Name;
        if (!rvItem.TotalPerMonthImage.IsNullOrWhiteSpace() ||
            !rvItem.DifferentStoresImage.IsNullOrWhiteSpace() ||
            !rvItem.SubCategoryImage.IsNullOrWhiteSpace() ||
            !rvItem.TransactionsPerMonthImage.IsNullOrWhiteSpace() ||
            !rvItem.AverageTradeValueImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level2'>" + mainCatName + "</li>");
        }
        //----------------- get images --------------------------//
        if (!rvItem.TotalPerMonthImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level3'>Totalt pr. måned</li>");
        }
        if (!rvItem.DifferentStoresImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level3'>Fordeling</li>");
        }
        if (!rvItem.SubCategoryImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level3'>Kategorier</li>");
        }
        if (!rvItem.TransactionsPerMonthImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level3'>Transaksjoner pr. måned</li>");

        }
        if (!rvItem.AverageTradeValueImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level3'>Gjennomsnittlig handlebeløp</li>");

        }

    }

}
//------------------ Inntekt menu --------------------------//
html.Append("<li class='list-header'>Inntekt</li>");
if (await db.InntektPerMonths.AnyAsync(n => n.CustomerId == customerId))
{
   html.Append("<li class='list-level2'>Inntekt per måned</li>");
}
if (await db.InntektSubCategories.AnyAsync(n => n.CustomerId == customerId))
{
   html.Append("<li class='list-level2'>Fordeling av inntekt</li>");
}
if (await db.InntektFordelings.AnyAsync(n => n.CustomerId == customerId))
{
   html.Append("<li class='list-level2'>Fordeling mellom inntekt og forbruk</li>");
}

//----------------- SPARING menu ------------------------//
if (db.SparingKommentarers.Any(n => n.CustomerId == customerId) ||
    db.SparingTabells.Any(n => n.CustomerId == customerId))
{
   html.Append("<li class='list-header'>Sparing</li>");

    if (await db.SparingKommentarers.AnyAsync(n => n.CustomerId == customerId))
    {
       html.Append("<li class='list-level2'>Kommentar</li>");
    }
    if (await db.SparingRads.AnyAsync(n => n.CustomerId == customerId))
    {
       html.Append("<li class='list-level2'>Sparetips</li>");
    }
    if (await db.SparingTabells.AnyAsync(n => n.CustomerId == customerId))
    {
       html.Append("<li class='list-level2'>Tabell</li>");
    }
}
//----------------- BUDSJETT menu ------------------------//
if (db.Budsjettkommentarers.Any() ||
    db.BudsjettGraf1s.Any() ||
    db.TotalBruks.Any() ||
    db.TotalRegningers.Any())
{
   html.Append("<li class='list-header'>Budsjett</li>");

    if (await db.Budsjettkommentarers.AnyAsync(n => n.CustomerId == customerId))
    {
       html.Append("<li class='list-level2'>Kommentar</li>");
    }
    if (await db.TotalBruks.AnyAsync(n => n.CustomerId == customerId))
    {
       html.Append("<li class='list-level2'>Totalt bruk</li>");
    }
    if (await db.TotalRegningers.AnyAsync(n => n.CustomerId == customerId))
    {
       html.Append("<li class='list-level2'>Total regninger</li>");
    }
    if (await db.BudsjettGraf1s.AnyAsync(n => n.CustomerId == customerId))
    {
       html.Append("<li class='list-level2'>Budsjett tall</li>");
    }
}
//----------------- OPPSUMMERING(Articles) menu ------------------------//
if (await db.Articles.AnyAsync(n => n.CustomerId == customerId))
{
   html.Append("<li class='list-header'>Oppsummering</li>");
}

//------------------ End of Menu -----------------------------------------------//
html.Append("</ul>");

//------------------ two blank pages (6-7) ---------------------------------------//


////------------------------------- Start generating inner pdf ---------------------------------------------//
////---------------------------------------Innledning------------------------------------------------------//
var customerforInnlending = await db.Innlednings.FirstOrDefaultAsync(inl => inl.CustomerId == customerId);
html.Append(header);

string textforInnlending = (customerforInnlending != null) ? customerforInnlending.Text : "<b>Empty Text</b>";

html.Append("<h1 class='menu-title'>Innledning </h1>" +
        "<div class='text-block'>" + textforInnlending + "</div>" +
        "<p class='break'></p>");
html.Append("<br>");


//-----------------------------------Fordeling-----------------------------------------------------------//
var fordelingList = await db.Fordelings.Where(cus => cus.CustomerId == customerId).ToListAsync();
foreach (var item in fordelingList)
{
    if (!item.TransactionsImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1> Fordeling av bruk og regninger </h1>");
       html.Append("<img class='report-img circlediagram' width='800px' height='auto' src='" + item.TransactionsImage + "'></img>");
       html.Append("<div class='text-block'>" + item.Text + "</div>");
    }
    if (!item.TopTransactionsImage.IsNullOrWhiteSpace())
    {
       html.Append("<img class='report-img stolpeddiagram mtop30' width='600px' height='auto' src='" + item.TopTransactionsImage + "'></img>");
    }
   html.Append("<div class='text-block break'></div>");

}

//---------------------------- Bruskonto title -----------------------------------------------------------//
imageArray = System.IO.File.ReadAllBytes(HttpContext.Server.MapPath("../Content/images/bruk_header.png"));
base64ImageRepresentation = Convert.ToBase64String(imageArray);
html.Append(header);
html.Append("<div class='break' style='display:block;padding-top:300px;padding-left:0px;'>");
html.Append("<img class='report-img' src='data:image/png;base64," + base64ImageRepresentation + "'></img>");
html.Append("</div>");



////------------------------------------------------Fordeling av Bruk------------------------------------------------------//
var fordelingBruk = await db.FordelingBruks.Where(cus => cus.CustomerId == customerId).ToListAsync();
foreach (var item in fordelingBruk)
{
    if (!item.MainCatImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1>Fordeling av Brukskonto </h1>");
       html.Append("<img class='report-img circlediagram' src='" + item.MainCatImage + "'></img>");
       html.Append("<div class='text-block'>" + item.Text + "</div>");
    }
    if (!item.TopMainCatImage.IsNullOrWhiteSpace())
    {
       html.Append("<img class='report-img stolpeddiagram mtop30' width='600px' height='auto' src='" + item.TopMainCatImage + "'></img>");
    }
   html.Append("<div class='text-block break'></div>");
}




//------------------------------------------------ ReportView Bruk------------------------------------------------------//
//--------------------------- Get transaction category id of selected area ---------------------------//
var tranCat = "Bruk";
var tranCatId = 0;
if (await db.TransactionCategories.AnyAsync(n => n.Name == tranCat))
{
    tranCatId = (await db.TransactionCategories.FirstAsync(n => n.Name == tranCat)).Id;

}
else
{
    throw new HttpException(404, "'" + tranCat + "' transaction category does not exist!");

}

var reportViewBrukList = db.ReportViews.AsParallel().Where(n => n.TransactionCategoryId == tranCatId && n.CustomerId == customerId).ToList();

foreach (var rvItem in reportViewBrukList)
{
    var mainCatName = (await db.Categories.FirstAsync(n => n.Parent == null && n.Id == rvItem.MainCategoryId)).Name;
    //----------------- get images --------------------------//
    if (!rvItem.TotalPerMonthImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1> Diverse " + mainCatName + "</h1>");
       html.Append("<img class='report-img img-size' src='" + rvItem.TotalPerMonthImage + "'></img>");
       html.Append("<div class='text-block break'>" + rvItem.TotalPerMonthText + "</div>");
    }
    if (!rvItem.DifferentStoresImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1> Fordeling av " + mainCatName + "</h1>");
       html.Append("<img class='report-img img-size' src='" + rvItem.DifferentStoresImage + "'></img>");
       html.Append("<div class='text-block break'>" + rvItem.DifferentStoresText + "</div>");


    }
    if (!rvItem.SubCategoryImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1> Fordeling på " + mainCatName + "</h1>");
       html.Append("<img class='report-img img-size' src='" + rvItem.SubCategoryImage + "'></img>");
       html.Append("<div class='text-block break'>" + rvItem.SubCategoryText + "</div>");
    }
    if (!rvItem.TransactionsPerMonthImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1>" + mainCatName + "</h1>");
       html.Append("<img class='report-img img-size' src='" + rvItem.TransactionsPerMonthImage + "'></img>");
       html.Append("<div class='text-block break'>" + rvItem.TransactionsPerMonthText + "</div>");

    }
    if (!rvItem.AverageTradeValueImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1> Antall transaksjoner pr. mnd på " + mainCatName + "</h1>");
       html.Append("<img class='report-img img-size' src='" + rvItem.AverageTradeValueImage + "'></img>");
       html.Append("<div class='text-block break'>" + rvItem.AverageTradeValueText + "</div>");

    }

}

//---------------------------- Regningskonto title -----------------------------------------------------------//
imageArray = System.IO.File.ReadAllBytes(HttpContext.Server.MapPath("../Content/images/regninger_header.png"));
base64ImageRepresentation = Convert.ToBase64String(imageArray);
html.Append(header);
html.Append("<div class='break' style='display:block;padding-top:300px;padding-left:0px;'>");
html.Append("<img class='report-img' src='data:image/png;base64," + base64ImageRepresentation + "'></img>");
html.Append("</div>");

//-------------------------------------------- Fordeling av regninger (page 39) ---------------------------------------------------------//
var fordelingRegninger = await db.FordelingRegningers.Where(cus => cus.CustomerId == customerId).ToListAsync();
foreach (var item in fordelingRegninger)
{
    if (!item.MainCatImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1>Fordeling av regninger </p>");
       html.Append("<img class='report-img circlediagram' width='800px' height='auto' src='" + item.MainCatImage + "'></img>");
       html.Append("<div class='text-block'>" + item.Text + "</div>");
    }
    if (!item.TopMainCatImage.IsNullOrWhiteSpace())
    {
       html.Append("<img class='report-img stolpeddiagram mtop30' width='600px' height='auto' src='" + item.TopMainCatImage + "'></img>");
    }
   html.Append("<div class='text-block break'></div>");
}


//------------------------------------------------ ReportView Regninger (p38-48)------------------------------------------------------//
//--------------------------- Get transaction category id of selected area ---------------------------//
tranCat = "Regning";
tranCatId = 0;
if (await db.TransactionCategories.AnyAsync(n => n.Name == tranCat))
{
    tranCatId = (await db.TransactionCategories.FirstAsync(n => n.Name == tranCat)).Id;

}
else
{
    throw new HttpException(404, "'" + tranCat + "' transaction category does not exist!");
}

var reportViewRegningerList = db.ReportViews.AsParallel().Where(n => n.TransactionCategoryId == tranCatId && n.CustomerId == customerId).ToList();

foreach (var rvItem in reportViewRegningerList)
{
    var mainCatName = (await db.Categories.FirstAsync(n => n.Parent == null && n.Id == rvItem.MainCategoryId)).Name;
    //----------------- get images --------------------------//
    if (!rvItem.TotalPerMonthImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1> Diverse " + mainCatName + "</h1>");
       html.Append("<img class='report-img img-size' src='" + rvItem.TotalPerMonthImage + "'></img>");
       html.Append("<div class='text-block break'>" + rvItem.TotalPerMonthText + "</div>");
    }
    if (!rvItem.DifferentStoresImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
        html.Append("<h1> Fordeling av " + mainCatName + "</h1>");
       html.Append("<img class='report-img img-size' src='" + rvItem.DifferentStoresImage + "'></img>");
       html.Append("<div class='text-block break'>" + rvItem.DifferentStoresText + "</div>");


    }
    if (!rvItem.SubCategoryImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1> Fordeling på " + mainCatName + "</h1>");
       html.Append("<img class='report-img img-size' src='" + rvItem.SubCategoryImage + "'></img>");
       html.Append("<div class='text-block break'>" + rvItem.SubCategoryText + "</div>");
    }
    if (!rvItem.TransactionsPerMonthImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1>" + mainCatName + "</h1>");
       html.Append("<img class='report-img img-size' src='" + rvItem.TransactionsPerMonthImage + "'></img>");
       html.Append("<div class='text-block break'>" + rvItem.TransactionsPerMonthText + "</div>");

    }
    if (!rvItem.AverageTradeValueImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1> Antall transaksjoner pr. mnd på " + mainCatName + "</h1>");
       html.Append("<img class='report-img img-size' src='" + rvItem.AverageTradeValueImage + "'></img>");
       html.Append("<div class='text-block break'>" + rvItem.AverageTradeValueText + "</div>");

    }

}


//---------------------------- Inntekter title -----------------------------------------------------------//
imageArray = System.IO.File.ReadAllBytes(HttpContext.Server.MapPath("../Content/images/inntekter_header.png"));
base64ImageRepresentation = Convert.ToBase64String(imageArray);
html.Append(header);
html.Append("<div class='break' style='display:block;padding-top:300px;padding-left:0px;'>");
html.Append("<img class='report-img' src='data:image/png;base64," + base64ImageRepresentation + "'></img>");
html.Append("</div>");

//---------------------------------------- Inntekt per måned  (p50) --------------------------------------------------------------//
if (await db.InntektPerMonths.AnyAsync(n => n.CustomerId == customerId))
{
    var innPerManed = await db.InntektPerMonths.FirstAsync(n => n.CustomerId == customerId);
    if (!innPerManed.InntektPerMonthImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1> Inntekt per måned </h1>");
       html.Append("<img class='report-img img-size' src='" + innPerManed.InntektPerMonthImage + "'></img>");
       html.Append("<div class='text-block break'>" + innPerManed.Text + "</div>");

    }

}

//---------------------------------------- Fordeling av inntekt  (p51) --------------------------------------------------------------//
if (await db.InntektFordelings.AnyAsync(n => n.CustomerId == customerId))
{
    var innFordelings = await db.InntektSubCategories.FirstAsync(n => n.CustomerId == customerId);
    if (!innFordelings.InntektSubCategoryImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1> Fordeling av inntekt  </h1>");
       html.Append("<img class='report-img img-size' src='" + innFordelings.InntektSubCategoryImage + "'></img>");
       html.Append("<div class='text-block break'>" + innFordelings.Text + "</div>");

    }
}


//---------------------------------------- Fordeling mellom inntekt og forbruk   (p52) --------------------------------------------------------------//
if (await db.InntektSubCategories.AnyAsync(n => n.CustomerId == customerId))
{
    var innSub = await db.InntektFordelings.FirstAsync(n => n.CustomerId == customerId);
    if (!innSub.InntektFordelingImage.IsNullOrWhiteSpace())
    {
       html.Append(header);
       html.Append("<h1> Fordeling mellom inntekt og forbruk  </h1>");
       html.Append("<img class='report-img img-size' src='" + innSub.InntektFordelingImage + "'></img>");
       html.Append("<div class='text-block break'>" + innSub.Text + "</div>");

    }
}

html.Append("</body></html>");

//---------- some code 
//........

var htmlStr = html.toString();
2

There are 2 best solutions below

4
On

Strings are immutable in .NET. You should use a StringBuilder instead of string.

Something like that:

StringBuilder sb = new StringBuilder(16 * 1024); // adapt memory reservation depending on document size
sb.Append("string_to_append");
...
return sb.ToString();

This technique is far more effective than +=

0
On

You can do following things to improve performance:

  1. Minimize the use of Append() on StringBuilder instance and call it in a chain fashion. This might lead to more call to append method. But that depends. You need to check and decide what is best for you.
  2. Replace var with exact type excepting anonymous type.
  3. Don't use async/await (Remove and watch if it impacts the performance) as I don't think that is beneficial here in your case. (This is optional)
  4. Put variable out of foreach loop.

Example :

html.Append("<div class='break' style='margin: 0 0 0 0px;'><img class='report-img' style='margin:150px 0 400px 0;' src='data:image/jpeg;base64,")
                .Append(base64ImageRepresentation)
                .Append("</img><p class='purpleText' style='color:#8E0033; font-size: 30px; margin:0; line-height:20px;'>")
                .Append( title)
                .Append("</p><p class='purpleText' style='color:#8E0033; font-size: 16px;'>")
                .Append( bigTitle)
                .Append("</p><p style='color:#980000; font-size: 50px; margin: 46px 0 0 0;'>")
                .Append(custName.firstName)
                .Append("<span>")
                .Append(custName.lastName)
                .Append("</span></p></div>");

Example of var: var tranCatIdBruk = 0; to int tranCatIdBruk = 0;

You have used var keyword inside foreach loop which will lead to decrease the performance (Believe me I have done practical on that and I was shocked at that time :D)

Example of variable outside foreach:

var mainCatName; // Replace var with string.
foreach (var rvItem in rvBrukItems)
    {
       mainCatName  = (await db.Categories.FirstAsync(n => n.Id == rvItem.MainCategoryId)).Name;

        if (!rvItem.TotalPerMonthImage.IsNullOrWhiteSpace() ||
            !rvItem.DifferentStoresImage.IsNullOrWhiteSpace() ||
            !rvItem.SubCategoryImage.IsNullOrWhiteSpace() ||
            !rvItem.TransactionsPerMonthImage.IsNullOrWhiteSpace() ||
            !rvItem.AverageTradeValueImage.IsNullOrWhiteSpace())
        {
           html.Append("<li class='list-level2'>" + mainCatName + "</li>");
        }
}

For StringBuilder Append() you can refer to below link which explains what happens internally:

String Builder best practices

Hope this will help you. Let me know if you have any other doubts.