Populate table column from code foreach row - c#

I recently started the transition from asp.net webforms to blazor server side.
I am still reading and learning about it, and as a side project in order to learn, i started to rewrite an application i made in webforms.
So, in the webforms, when i had to present very big amounts of data coming from ms sql server, i used asp:repeater to populate the main values, and then with nested update panels i managed to have the basic results almost instantly, and after 1-2 seconds, the other columns were populated.
I try to do something similar with Blazor, but with no luck at all.
My code for the main values is
<tbody>
#if(antists==null)
{
}
else
{
#foreach (var antist in antists)
{
string? trdr = "405";
string? mtrl = antist.Mtrl;
string? trdbusiness = "2001";
string? mtrpcategory = antist.Mtrpcategory;
string? mtrmanfctr = antist.Mtrmanfctr;
string image = "https://zerog01.b-cdn.net/" + antist.Image_guid + ".jpg";
//getfldasync(trdr, mtrl, trdbusiness, mtrpcategory, mtrmanfctr);
<tr>
<td style="text-align: center">
<a data-fancybox href=#image>
<img id="img_eikona_in" runat="server" class="img-fluid" style="max-height:100px;"
src=#image onerror="this.onerror=null; this.src='/webimages/no-image.jpg'" /></a>
</td>
<td>#antist.Code.ToString() <br>
#antist.Name</td>
<td>#fld</td>
</tr>
}
}
</tbody>
My goal is to populate #fld from a stored procedure. If i use code to do this on the fly as the rows are being created, it is very slow. (same problem as webforms)
I tried to use async method, but no luck. The closer i got was to have System.Threading.Tasks.Task`1[System.String] shown in the column of #fld. I didn't keep the exact code, but it was something like
public async Task<string> getfldasync(string trdr, string mtrl, string trdbusiness, string mtrpcategory, string mtrmanfctr )
SqlCommand cmd = new SqlCommand(str, con_digi);
cmd.Parameters.AddWithValue("#trdr", trdr);
cmd.Parameters.AddWithValue("#mtrl", mtrl);
cmd.Parameters.AddWithValue("#trdbusiness", trdbusiness);
cmd.Parameters.AddWithValue("#mtrpcategory", mtrpcategory);
cmd.Parameters.AddWithValue("#mtrmanfctr", mtrmanfctr);
await con_digi.OpenAsync();
var scalar = cmd.ExecuteScalarAsync();
fld = Convert.ToString(scalar);
await con_digi.CloseAsync();
return fld;
>not a sp, but i wanted to make changes in order to test the code.
Is there another method i miss for presenting that kind of data? Or should i keep on trying to make async call?
Edit: I uploaded the code where i used datatable instead of list for the initial databind of the table.

Related

Asynchronously calling an external REST API from controller or view?

I've been trying to wrap my head around this for too long. This is my first time with C# and .NET and apart from dabbling with C++ almost 15 years ago I have no programming experience. REST APIs and asynchronicity are new concepts to me.
I'm trying to make a scoreboard page in .NET MVC 5 consisting of a simple table with rows containing values retrieved from a MySQL database.
Each row in the database table represents a unique player along with his various stat values. I've created a model to represent each player, PlayerRow, and am passing a List<PlayerRow> to my view where the table gets generated via #foreach (PlayerRow playerrow in Model).
This is my current ActionResult PlayerList():
public class HomeController : Controller
{
public ActionResult PlayerList()
{
List<PlayerRow> PlayerRows = new List<PlayerRow>();
string constr = ConfigurationManager.AppSettings["MySQLConnStr"];
using (MySqlConnection con = new MySqlConnection(constr))
{
string query = "SELECT * FROM `rankme`";
using (MySqlCommand cmd = new MySqlCommand(query))
{
cmd.Connection = con;
con.Open();
using (MySqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
PlayerRows.Add(new PlayerRow
{
TableID = rdr["id"].ToString(),
LegacySteamID = rdr["steam"].ToString(),
Steam64 = ConvertToSteam64(rdr["steam"].ToString()),
Name = rdr["name"].ToString(),
IP = rdr["lastip"].ToString(),
Score = Convert.ToInt32(rdr["score"]),
etc etc...
});
}
}
con.Close();
}
}
return View(PlayerRows);
}
}
Relevant content from my PlayerList.cshtml:
#foreach (PlayerRow playerrow in Model)
{
<tr>
<th scope="row" class="avatar" id="#playerrow.Steam64"><img src="~/Content/placeholder.png"</th>
<td>#playerrow.Name</td>
<td>#playerrow.Score</td>
<td>#playerrow.Kills</td>
<td>#playerrow.Deaths</td>
<td>#playerrow.Assists</td>
etc etc...
</tr>
}
The MySQL connection works just fine and the table gets displayed just how I want it to. (albeit not quite polished, yet)
Picture of my current table
What I would like to do next is replace the placeholder avatar in each table row with the players avatar retreived via the Steam API. Each players avatar url can be retreived from the Steam API by calling https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key=_API_KEY_&steamids=_STEAMID64_ which returns a json response:
{
"response":{
"players":[
{
"steamid":"EXAMPLE_STEAMID",
"communityvisibilitystate":3,
"profilestate":1,
"personaname":"EXAMPLE_NAME",
"commentpermission":1,
"profileurl":"EXAMPLE_STEAM_PROFILE_URL",
"avatar":"https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/EXAMPLE_AVATAR.jpg",
"avatarmedium":"https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/81/EXAMPLE_AVATAR_medium.jpg",
"avatarfull":"https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/EXAMPLE_AVATAR_full.jpg",
"lastlogoff":1586222967,
"personastate":0,
"realname":"EXAMPLE_REALNAME",
"primaryclanid":"103582",
"timecreated":1070200624,
"personastateflags":0,
"loccountrycode":"US"
}
]
So I guess that requires me to make the request using HttpClient() and awaiting the response. That's where I get lost because I can't seem to await the response outside of an async method. Maybe using AJAX (which I've never used) would be more appropriate?
Could I create an async method to consume the Steam API? When and from where should that method be called?
To call an async method from a controller you can simply change your signature to
public async Task<IActionResult> PlayerList(CancellationToken cancellationToken)
From here, you'll be able to call any async method you like.
I don't really know the Steam API so if you're going to be making lots of network calls it might be better to do this on the Browser/Javascript side (lazy-loading the avatars as the browser scrolls into view).

itextsharp c# need a perfect method for page break

I am using itextsharp library.I design an HTML page and convert to PDF .in that case some table are not split perfectly and row also not split correctly
table.keepTogether;
table.splitRows;
table.splitLate
I try this extension but it does not work correctly it mess my CSS and data in PDF. if you have method..answer me:)
finally i got it
public class TableProcessor : Table
{
const string NO_ROW_SPLIT = "no-row-split";
public override IList<IElement> End(IWorkerContext ctx, Tag tag, IList<IElement> currentContent)
{
IList<IElement> result = base.End(ctx, tag, currentContent);
var table = (PdfPTable)result[0];
if (tag.Attributes.ContainsKey(NO_ROW_SPLIT))
{
// if not set, table **may** be forwarded to next page
table.KeepTogether = false;
// next two properties keep <tr> together if possible
table.SplitRows = true;
table.SplitLate = true;
}
return new List<IElement>() { table };
}
}
use this class and
var tagfac = Tags.GetHtmlTagProcessorFactory();
tagfac.AddProcessor(new TableProcessor(), new string[] { HTML.Tag.TABLE });
htmlContext.SetTagFactory(tagfac);
integrate this method with htmlpipeline context
this method every time run when the tag hasno-row-split.if it contain key it will make kept by keep together keyword to make page breaking
html
<table no-row-split style="width:100%">
<tr>
<td>
</td>
</tr>
</table>

Return a ViewBag ViewModel back to Controller in BeginForm Post method in the View

I have a View which displays a ViewModel on the page. I want to allow the user to press a button to create a CSV file which is then emailed to them. I have the POSt working but the ViewModel being sent back is always empty even though the page clearly show many rows.
This is part of the View in question:
<table style="width:99%" cellpadding="3" class="ContentTable" border="1" align="center">
#using (Html.BeginForm("SubmitExcel", "AllRecognition", new { AllRecognitions = ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel }, FormMethod.Post, new { id = "submitExcel" }))
{
<tr>
<td style="padding:3px;">
<input type="submit" name="BtnSubmitExcel" id="BtnSubmitExcel" value="Export to Excel" />
</td>
</tr>
}
<tr style="background-color:#5D7B9D;color:white;">
<th style="width:4%;padding:3px;font-size:12px;">Date</th>
<th style="width:8%;padding:3px;font-size:12px;">Employee</th>
<th style="width:8%;padding:3px;font-size:12px;">Recognized By</th>
<th style="width:6%;padding:3px;font-size:12px;">5-Star Standard</th>
<th style="width:70%;padding:3px;font-size:12px;">Description</th>
<th style="width:4%;padding:3px;font-size:12px;">Points</th>
</tr>
#{
if (ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel != null)
{
foreach (Recognition.ViewModels.AllRecognitionViewModel item in ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel)
{
#:<tr>
#:<td style="width:4%;padding:3px;font-size:12px;">#item.Date</td>
#:<td style="width:8%;padding:3px;font-size:12px;">#item.Employee</td>
#:<td style="width:8%;padding:3px;font-size:12px;">#item.RecognizedBy</td>
#:<td style="width:6%;padding:3px;font-size:12px;">#item.FiveStarStandard</td>
#:<td style="width:70%;padding:3px;font-size:12px;">#item.Description</td>
#:<td style="width:4%;padding:3px;font-size:12px;">#item.Points</td>
#:</tr>
}
}
}
</table>
This is the controller side receiving the POST method:
public ActionResult SubmitExcel(List<ViewModels.AllRecognitionViewModel> AllRecognitions)
{
ViewBag.NoSearch = "block";
ViewBag.SupervisorSearch = "none";
ViewBag.DepartmentSearch = "none";
ViewBag.EmployeeSearch = "none";
DataTable dtAllRecognitions = Base.SQLHelper.ConvertListToDataTable(AllRecognitions.ToList());
DataSet dsAllRecognitions = new DataSet();
dsAllRecognitions.Tables.Add(dtAllRecognitions);
FHSBase.FHS.DataHelper.SendMeExcelFile(dsAllRecognitions, "Recognitions", CurrentUser);
ViewModels.AllRecognitionBigViewModel AllRecognitionBigViewModel = new ViewModels.AllRecognitionBigViewModel();
AllRecognitionBigViewModel.AllRecognitionViewModel = null;
Models.DateRange DateRange = new Models.DateRange();
DateRange.fromDate = DateTime.Today.Date;
DateRange.toDate = DateTime.Today.Date;
AllRecognitionBigViewModel.DateRange = DateRange;
ViewBag.AllRecognitionBigViewModel = AllRecognitionBigViewModel;
List<SelectListItem> empList = new List<SelectListItem>();
string VPath = "Index";
return View(VPath, empList);
}
The "AllRecognitions" view model is empty in the ActionResult but isn't empty in the view itself. How can I get the current view model back to the ActionResult (SubmitExcel) with the current values seen in the View?
Will this work for you? It's a different approach. Since you are not manipulating anything in your view and just want to export to Excel, why not just put your results in TempData instead of ViewBag and then retrieve it upon the POST? TempData is good in memory for that one trip back.
So, in your initial controller render, do:
TempData["AllRecognition"] = ThisIsMyAllRecognitionViewModelData;
Then when they submit to excel, that data is still in Temp.
public ActionResult SubmitExcel()
{
var MyDataMadeIt = TempData["AllRecognition"];
// do some stuff
}
Your form is empty. So no data is being posted to the server.
When you submit a form, it doesn't send the entire page to the server. (How would the server even know what to do with the HTML to get the values you want from it?) It sends key/value pairs from form elements. And you have only one form element:
<input type="submit" name="BtnSubmitExcel" id="BtnSubmitExcel" value="Export to Excel" />
So only that one key/value is being sent to the server.
The first thing you need to do is wrap your form around the data that you want to post to the server. So basically around your entire table.
Next, you'll need to emit actual form elements. The text on the page isn't usable data, it's just page content. Since the model is a List<ViewModels.AllRecognitionViewModel> then you should be able to do it with a simple modification to your loop. So instead of this:
foreach (Recognition.ViewModels.AllRecognitionViewModel item in ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel)
You would want this:
for (var i = 0; i < ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel.Count(); i++)
The form elements can be hidden, so you don't change the current UX in your page. Maybe something like this:
#Html.HiddenFor(x => x[i].Date)
or perhaps:
#Html.HiddenFor(x => x.AllRecognitionViewModel[i].Date)
I'm actually guessing a bit here, since I'm much more accustomed to using the model instead of the ViewBag for this. (Which you may want to try using instead, it'll probably make things simpler.) You may need to do some debugging to determine exactly what's going on in the server-side code here.
Ultimately, what you're looking to see in your client-side code are elements similar to this:
<input type="hidden" name="AllRecognitionViewModel.Date[0]" value="..." />
So, if the above #Html.EditorFor suggestions don't work, you can always do it manually. In the loop it might look something like this:
<input type="hidden" name="AllRecognitionViewModel.Date[#i]" value="#ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel[i].Date" />
The key thing to notice is the name attribute. By sending these "arrays" of key/value pairs to the server, the model binder should be able to construct the List<ViewModels.AllRecognitionViewModel>.

Using C# Script in SharePoint Page

I've done quite a bit of searching (several hours actually) but I haven't been able to get this working. Basically, I have this button:
<asp:Button runat="server" Text="Go!" id="go" onClick="getDoc()" />
and this block of script:
<script type="c#" runat="server">
public void getDoc(object sender, EventArgs e) {
// Test to see if function was running (it's not...)
DocFrame.Attributes["src"] = "http://www.google.com";
// Get the current state of the dropdowns
String dropYear = (String)Year.SelectedValue;
String dropDiv = (String)Division.SelectedValue;
String dropControl = (String)Control.SelectedValue;
String dropQuart= (String)Quarter.SelectedValue;
// Get the Site where the list is
using (SPSite siteCol = new SPSite("http://portal/Corporate/IT/")) {
using (SPWeb web = siteCol.RootWeb){
// Get the list items we need
SPListItemCollection items = list.GetItems("Year", "Division", "Control", "Quarter");
SPListItem item = null;
// Loop through them until we find a matching everything
foreach (SPListItem it in items){
if(it.Year == dropYear && it.Division == dropDiv && it.Control == dropControl && it.Quarter == dropQuart){
item = it;
break;
}
}
// Assign the item as a string
String URL = (String)item["Title"];
// Set the iframe to the new URL
DocFrame.Attributes["src"] = URL;
}
}
}
It's all in the page where this is happening, please keep in mind that I've been using sharepoint for less than a week and have only ever coded in C++, so I could be doing everything horribly wrong. Anyway, it seems that getDoc() is never even getting called, so can anyone point out what I'm doing wrong?
Instead of
onClick="getDoc()"
you should do
OnClick="getDoc"
That's the proper way to wire an up an event.
By the way, you should consider following C# Naming Guidelines. If you were using better naming, it might look like this:
<asp:Button runat="server" Text="Go!" id="GoBtn" onClick="GoBtn_Click" />
Common practice convention is to append the event name after the ID of the control. It's not required, but it looks cleaner and other developers like to see that when they look at your code.
Also, DocFrame.Attributes["src"] = "http://www.google.com"; is not a good way to see if the function is running. It doesn't update the page in realtime, as the entire server side function executes, then the results are sent to the client. Instead, use your IDE's debugging tools to hook up to the server and set code breaks etc. Or what I do is have the code send me an email, I created a little utility library for that.

asp.net MVC3 C#: Passing query as a parameter and displaying result

I am new to asp.net , C# and building an MVC application based on the popular Music Store application.
I have my basic navigation ready and I have reached a point where I am drawing a complete blank. Basically, my asp page displays a SQL query (which is saved in SQL DB on same machine)
Need:
I need to have a button next to this query which when clicked, connects to another DB through OLEDB, and runs the query and shows result in a pop up window.
Questions:
How do I pass the query (which is being fetched from DB) as a parameter to code below and How do I make the results pop up in a window.
Can you please point me in correct direction. The code below is from a stand alson asp page which i used for testing connections etc. basically i need to pass the query as a parameter (replacing query seen below) and have the result in a pop window.
<%# Import Namespace="System.Data.OleDb" %>
<%# Import Namespace="System.Data.Odbc" %>
<script runat="server">
sub Page_Load
Dim dbconn, sql, dbcomm, dbread
dbconn = New OleDbConnection("Provider=xxxx;Password=xxxx;User ID=xxxx;Data Source=xxxx;Initial Catalog=xxxx;Port=xxxx;")
dbconn.Open()
sql = "Select ID from TABLE1"
dbcomm = New OleDbCommand(sql, dbconn)
dbread = dbcomm.ExecuteReader() <%-- Call this method within oledbcommand--%>
If dbread.Read = False Then
MsgBox("No Data Check")
Else
Response.Write("<table>")
Do While dbread.Read()
Response.Write("<tr>")
Response.Write("<td>")
Response.Write(dbread(0))
Response.Write("</td>")
Response.Write("</tr>")
Loop
Response.Write("</table>")
End If
dbconn.Close()
end sub
</script>
ADDITIONAL DETAILS
CONTROLLER CLASS
.
.
public ActionResult DisplayResult(String Qry)
{
List<QuerySet> QueryToExecute = new List<QuerySet>();
return View(QueryToExecute);
VIEW that provides this contoller with DATA, this is query that is fetched from my SQL DB and should be executed to a separate DB on a separate server.
<ul>
#foreach (var ShowQueries in Model.Queriess)
{
<li>
#Html.ActionLink(ShowQueries.Query, "DisplayResult", new { Qry = ShowQueries.Query })
</li>
}
ISSUE:
How should I use a view named 'DisplayResult' which handles the query fetched by view above and executes it agaisnt another DB.
I was hoping I can use a Webform view rather than a razor view but either way i am not able to pass the parameter
Any ideas are appreciated
The point of MVC is to move data connections out of the View (aspx page) and into a Controller.
Read some more MVC tutorials, and buy a book or two. You should actually populate the data into a viewmodel on the controller, and then pass that viewmodel to the view. This way, the view knows nothing about how to get the data -- it already got it from the controller.
Views should have the responsibility of displaying data to users over the web, not getting the data or manipulating it directly.
With that aside, here is how you would do it:
Pass the query as a string to an Action Method on a Controller (using HTTP POST or GET) using AJAX (i.e. jQuery $.ajax() method).
Have the action method return the HTML for your popup window, using a Partial View. You could also return Json, but I think HTML / partial view would be easier in this case. This is the method that will do your OLE DB connection, and execute the query.
In your $.ajax() success function callback, write javascript that will popup a new dialog with the partial view HTML that was returned by the controller action method.
You could create a class to hold the data you want to display:
namespace sample {
class viewList
{
public string field1 {get;set;}
...
}
}
and create a list to hold your results in your controller:
List<viewList> theList = new List<viewList>();
//Populate dbread here...
while (dbread.Read())
{
viewList listData = new viewList();
listData.field1 = (dataType)dbread[0]; //Convert to your data type
theList.Add(listData);
}
and pass this to the view:
return view(theList);
Then in your model (of model type viewList) display your results in a table:
#model sample.viewList
<table>
#foreach (var item in Model)
{
<tr>
<td>#item.field1</td>
</tr>
}
</table>
ALTERNATIVE
To display in popup, put the list into the ViewBag like this:
List<viewList> theList = new List<viewList>();
//Populate dbread here...
while (dbread.Read())
{
viewList listData = new viewList();
listData.field1 = (dataType)dbread[0];
theList.Add(listData);
}
ViewBag.Items = theList;
Then in your view:
<script type="text/javascript">
$(function() {
var array = #Html.Raw(Json.Encode(ViewBag.Items));
//Construct your table using the array here...
alert(theConstructedTable);
});
</script>

Categories