Server side pagination with MVC C# and MySql - c#

I didn't understand how does server-side pagination works with MySql and Datatable in a C# MVC.
I created a Controlled in C#, in which I established the connection with a MySql database (I followed this example in order to do that):
public ActionResult connectDB()
{
const string DB_CONN_STR = "Server=MyServer;Port=MyPort;Uid=MyUid;Database=MyDB;";
MySqlConnection cn = new MySqlConnection(DB_CONN_STR);
try
{
string sqlCmd = "select * from t_documento";
MySqlDataAdapter adr = new MySqlDataAdapter(sqlCmd, cn);
adr.SelectCommand.CommandType = CommandType.Text;
DataTable dt = new DataTable();
adr.Fill(dt); //opens and closes the DB connection automatically !! (fetches from pool)
return Content(JsonConvert.SerializeObject(dt).ToString());
}
catch (Exception ex)
{
Console.WriteLine("{oops - {0}", ex.Message);
return Content(ex.ToString());
}
finally
{
cn.Dispose(); // return connection to pool
}
}
However, in this way, I retrieve all the records stored in that table, but I want to fill the Datatable (the inizialization of my datatable is located in a cshtml page) by implementing the pagination.
I read a lot of articles but I didn't found a clear example with a MySql DB.
Can someone help me, please?
Thank you!

Try this Example of server side pagination.
/Controllers/ProductController.cs
public class ProductController : Controller
{
public object Index(int? page)
{
var products = MyProductDataSource.FindAllProducts(); //returns IQueryable<Product> representing an unknown number of products. a thousand maybe?
var pageNumber = page ?? 1; // if no page was specified in the querystring, default to the first page (1)
var onePageOfProducts = products.ToPagedList(pageNumber, 25); // will only contain 25 products max because of the pageSize
ViewBag.OnePageOfProducts = onePageOfProducts;
return View();
}
}
/Views/Products/Index.cshtml
#{
ViewBag.Title = "Product Listing"
}
#using PagedList.Mvc; //import this so we get our HTML Helper
#using PagedList; //import this so we can cast our list to IPagedList (only necessary because ViewBag is dynamic)
<!-- import the included stylesheet for some (very basic) default styling -->
<link href="/Content/PagedList.css" rel="stylesheet" type="text/css" />
<!-- loop through each of your products and display it however you want. we're just printing the name here -->
<h2>List of Products</h2>
<ul>
#foreach(var product in ViewBag.OnePageOfProducts){
<li>#product.Name</li>
}
</ul>
<!-- output a paging control that lets the user navigation to the previous page, next page, etc -->
#Html.PagedListPager( (IPagedList)ViewBag.OnePageOfProducts, page => Url.Action("Index", new { page }) )

Related

When using Abp Modal tag helper with widget at the same time, the js file bind to widget cannot be loaded

In my project, i need to create an reusable "food select list" component, this component will use as food index page and food select modal window.
I created a FoodsViewComponent which inherit from AbpViewComponent and also marked as 'Widget', below are the code of this component:
[Widget(
StyleFiles = new[] { "/Pages/Shared/Components/FoodList/Default.css" },
ScriptTypes = new[] { typeof(FoodListWidgetScriptBundleContributor) },
RefreshUrl = "Widget/LoadFoodListComponent"
)]
[ViewComponent(Name = "FoodList")]
public class FoodsViewComponent : AbpViewComponent
{
private readonly IFoodAppService _foodAppService;
public FoodsViewComponent(IFoodAppService foodAppService)
{
_foodAppService = foodAppService;
}
public async Task<IViewComponentResult> InvokeAsync(
string keyword,
FoodSelectMode mode = FoodSelectMode.List,
string onclick = null,
string foodSelect = null,
string processSelect = null,
int page = 1,
int pageSize = 2)
{
PagedKeywordSearchRequestInputDto input = new()
{
Keyword = keyword,
MaxResultCount = pageSize,
SkipCount = (page - 1) * pageSize
};
PagedResultDto<FoodBaseDto> results = await _foodAppService.GetPagedListAsync(input);
ViewBag.Keyword = keyword;
ViewBag.Page = page;
ViewBag.PageSize = pageSize;
ViewBag.FoodSelectMode = mode;
ViewBag.FoodSelect = foodSelect;
ViewBag.Onclick = onclick;
ViewBag.ProcessSelect = processSelect;
return View(results);
}
}
And the FoodListWidgetScriptBundleContributor code as below:
public class FoodListWidgetScriptBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files
.AddIfNotContains(new string[]
{
"/Pages/Shared/Components/FoodList/Default.js" ,
"/libs/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.js"
});
}
}
The razor page of this component is as below:
#using Chriswu00.HealthEngine.FoodNutrition.Web;
#model PagedResultDto<FoodBaseDto>
#{
int p = ViewBag.Page;
int pageSize = ViewBag.PageSize;
string keyword = ViewBag.Keyword;
FoodSelectMode mode = ViewBag.FoodSelectMode;
string foodSelect = ViewBag.FoodSelect;
string componentId = $"foodsWidget_{mode}";
string onclick = ViewBag.Onclick;
string processSelect = ViewBag.ProcessSelect;
}
<div id="#componentId">
#foreach (var food in Model.Items)
{
<food-card food-id="#food.Id"
food-name="#food.Name"
food-click="#onclick"
food-select-mode="#mode"
food-select="#foodSelect"></food-card>
}
<boot-paginator link-url="/Widget/LoadFoodListComponent?keyword=#keyword&mode=#mode&foodSelect=#foodSelect&processSelect=#processSelect"
page="#p"
page-size="#pageSize"
total-items="#Model.TotalCount">
<ajax-options update-target-id="#componentId"
on-begin=""
on-complete="">
</ajax-options>
</boot-paginator>
#if (mode == FoodSelectMode.MultipleSelect ||
mode == FoodSelectMode.SingleSelect)
{
<abp-button button-type="Primary" onclick="#processSelect">Process Select</abp-button>
}
</div>
'food-card' is the custom html tag helper i created for display food item and the 'boot-paginator' is the custom html tag helper i created for pagination which has the ability to load and refresh 'the next page' by ajax.
Those component works fine, until i made a 'Modal Page' as 'Food selector'.
The Scenario is: My project is a Food and Nutrition database, user can create food and the system provids the api for getting the nutrition info of those foods. I defined two different types of food, one is just called 'Food' and the other is called 'Composite Food'. As the name suggests,the 'Composite Food' is the combination of more than one food, so during the food creation process, user can select multiple existing foods as the 'food facts' for the new food.
So i want to use a bootstrap modal window with a 'paginated food list' as the food fact selection UI, which i can integrate to the composite food creation and update pages.
I follow the 'Modals' chapter from the abp official docs (https://docs.abp.io/en/abp/6.0/UI/AspNetCore/Tag-Helpers/Modals) and create a 'FoodListModal' razor page as below:
#page
#using Microsoft.AspNetCore.Mvc.Localization
#using Chriswu00.HealthEngine.FoodNutrition.Localization;
#using Chriswu00.HealthEngine.FoodNutrition.Web.Pages.Foods;
#model Chriswu00.HealthEngine.FoodNutrition.Web.Pages.Foods.FoodListModalModel
#inject IHtmlLocalizer<FoodNutritionResource> Localizer
#{
Layout = null;
}
<abp-modal>
<abp-modal-header title="#Localizer[$"Food{Model.FoodSelectMode}"].Value"></abp-modal-header>
<abp-modal-body>
#await Component.InvokeAsync("FoodList", new
{
mode = Model.FoodSelectMode,
onclick = Model.Onclick,
foodSelect = Model.FoodSelect,
pageSize = 10,
processSelect = Model.ProcessSelect
})
</abp-modal-body>
</abp-modal>
After i integrate this modal to the composite food creation page, everything works fine except the pagination, instead of refresh within the food list, it reload the whole page. I guessed it is the problem of loading the jquery.unobtrusive-ajax.js. Because the 'Food List component' contains two js files, Default.js and the jquery.unobtrusive-ajax.js, To confirm that my guess was correct, i added an alert command into Default.js. After i tested, the Food Index page which also integrated the Food List component works fine and shows the alert when click on the pagination. But the 'Food Selector Modal' didn't shows the alert and reload the whole page.
That's what I'm currently investigating. Is this a bug of the framework? Or am I missing some knowledge point?

ActionResult Create with conditions

im doing an MVC with CRUDS.
this is my code.
[HttpPost]
public ActionResult Create([Bind(Include = "FileStatusID, Name, MinValue, MaxValue")] fileStatusModel FILeStatusModel, TBL_FileStatus tBL_FileStatus) //include tem os valores que vamos inserir na view
{
var userID = ((SessionModel)Session["SessionModel"]).UserID; // get current user id
if (ModelState.IsValid)
{
TBL_FileStatus item = new TBL_FileStatus()
{
Name = FILeStatusModel.Name,
MinValue = FILeStatusModel.MinValue,
MaxValue = FILeStatusModel.MaxValue,
Ative = true,
CreateDate = DateTime.Now,
CreateBy = userID
};
db.TBL_FileStatus.Add(item);
db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
I want to create a status with special conditions.
I have a table with ID, status, minvalue and maxvalue and have an interval of numbers in those values.
I want to create another status out of the range of those numbers.
ex: minvalue: 20 maxvalue: 40
So.. When i create a new status, if i put numbers inside that range, its say a message like "already exist in that range", if not, it creats de status.
thanks
1. Rendering layout page from ActionResult (using Controller. View extension method)
The Controller. View method has two extension methods, using these
extension methods we can pass a master page (layout page) name and
render a layout page based on a condition.
Example Code
public ActionResult About()
{
return View("About","_otherLayout");
}
public ActionResult OtherAbout()
{
string myName = "Jignesh Trivedi";
return View("About", "_otherLayout", myName);
}
2. Using _ViewStart.cshtml Page
The Controller. View method has two extension methods, using these
extension methods we can pass a master page (layout page) name and
render a layout page based on a condition.
Using the _ViewStart.cshtml page, we can change the layout page based
on a condition.
Example Code
#{
var controller = HttpContext.Current.Request.RequestContext.RouteData.Values["Controller"].ToString();
string layout = "";
if (controller != "Home")
{
layout = "~/Views/Shared/_otherLayout.cshtml";
}
else
{
layout = "~/Views/Shared/_Layout.cshtml";
}
Layout = layout;
}
We can also create multiple _ViewStart.cshtml pages. The file
execution is dependent upon the location of the file within the folder
hierarchy and the view being rendered. The MVC Runtime will first
execute the code of the _ViewStart.cshtml file located in the root of
the Views folder.
3. Define the Layout page in each view
We can override the default layout rendering by setting the Layout
property of the View using the following code.
#{
Layout = "~/Views/Shared/_otherLayout.cshtml";
ViewBag.Title = "About Us";
}
As presented in the article by : Jignesh Trivedi at
https://www.c-sharpcorner.com/UploadFile/ff2f08/rendering-layouts-base-on-condition-in-Asp-Net-mvc/

Pagination in jquery data table destroy partial view

I have table that contains data and links that open partial view with related data. I use jquery datatable, when I'm on first page everything works fine, but if I go to the next pages and cliked link I dont have beauty partial view, page return only source code from partial view file and skips _Layout.cshtml.
What should I do to make the code work correctly?
Controller:
public ActionResult KsiazkiZlecenia(int zlecKompletID, string magazynID)
{
SystemMagazynowy ksiazki = new SystemMagazynowy();
var listaksiazek = ksiazki.PobierzInfoKsiazek(zlecKompletID, magazynID);
ViewBag.ksiazki = listaksiazek;
return PartialView();
}
Link:
#Html.ActionLink("Książki", "KsiazkiZlecenia", new { zlecKompletID = item.ZlecKompletID, magazynID = ViewBag.MagazynID }, new { #data_modal = "" })

Cannot update entity framework model

I have spent nearly seven hours to figure this out and couldn't come up with a solution. So here am I, sharing this problem with you.
Please note that the following example is a simplification and subset of my original project. I tried to simplify it as much as possible for you.
To start, I have two business models:
The following EDMX diagram is as follows:
I am using MVC 4 and I have a simple page where you can enter home and away team names respectively and a save button to save these teams and the match:
CSHTML
#model TestEF.Data.Match
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>NewMatch</title>
</head>
<body>
<div>
Status: #ViewBag.Status
</div>
<div id="NewMatchFormContainer">
#using (Ajax.BeginForm(new AjaxOptions() { Url = "/Match/NewMatch", UpdateTargetId = "NewMatchFormContainer" }))
{
#Html.ValidationSummary(false)
#Html.TextBox("HomeTeamName", "", new { Name = "HomeTeam.TeamName" });
#Html.TextBox("AwayTeamName", "", new { Name = "AwayTeam.TeamName" });
<input type="submit" value="Save" />
}
</div>
</body>
</html>
Controller
public class MatchController : Controller
{
TestEFEntities _dbContext = new TestEFEntities();
public ActionResult Index()
{
return View();
}
public ActionResult NewMatch()
{
return View();
}
[HttpPost]
public ActionResult NewMatch(Match matchData)
{
try
{
if (ModelState.IsValid)
{
using (TransactionScope ts = new TransactionScope())
{
string homeTeamName = matchData.HomeTeam.TeamName;
Team existingHomeTeam = _dbContext.Teams.SingleOrDefault(i => i.TeamName == homeTeamName);
Team homeTeam = existingHomeTeam ?? matchData.HomeTeam;
homeTeam.UpdatedDate = DateTime.Now;
if (existingHomeTeam == null)
{
_dbContext.AddToTeams(homeTeam);
}
else
{
_dbContext.ObjectStateManager.ChangeObjectState(homeTeam, System.Data.EntityState.Modified);
}
string awayTeamName = matchData.AwayTeam.TeamName;
Team existingAwayTeam = _dbContext.Teams.SingleOrDefault(i => i.TeamName == awayTeamName);
Team awayTeam = existingAwayTeam ?? matchData.AwayTeam;
awayTeam.UpdatedDate = DateTime.Now;
if (existingAwayTeam == null)
{
_dbContext.AddToTeams(awayTeam);
}
else
{
_dbContext.ObjectStateManager.ChangeObjectState(awayTeam, System.Data.EntityState.Modified);
}
matchData.HomeTeam = homeTeam;
matchData.AwayTeam = awayTeam;
_dbContext.AddToMatches(matchData);
_dbContext.SaveChanges();
ts.Complete();
}
ViewBag.Status = "Success";
return PartialView(matchData);
}
else
{
ViewBag.Status = "Invalid input.";
return PartialView(matchData);
}
}
catch (Exception ex)
{
ViewBag.Status = "Error: " + (ex.InnerException != null ? ex.InnerException.Message : ex.Message);
return PartialView(matchData);
}
}
}
As you can see inside the controller, the entered team name is compared to those in the database. If one exists, it is to be updated; else inserted. There are no problems with inserts but when an existing team name is entered inside a textbox, I get the following error message:
Cannot insert the value NULL into column 'UpdatedDate', table
'TestEF.dbo.Teams'; column does not allow nulls. INSERT fails. The
statement has been terminated.
I get this error even though inside the controller, I explicitly set the UpdateDate for records that need to be updated and set its state to Modified. However the error message says as if UpdateDate field was not set. I debugged and made sure the fields are updated correctly but in SQL Profiler UpdateDate is not set. I am very confused.
I can share the full source code if needed.
UPDATE I suspect it has something to do with Attach/Detach but I am not sure.
UPDATE 2 I have simplified the code to see whether it works and it does. Then why does the original code not work?
Team homeTeam = new Team() { TeamId = 1 };
Team awayTeam = new Team() { TeamId = 2 };
_dbContext.Teams.Attach(homeTeam);
homeTeam.UpdatedDate = DateTime.Now;
_dbContext.Teams.Attach(awayTeam);
awayTeam.UpdatedDate = DateTime.Now;
Match newMatch = new Match()
{
HomeTeam = homeTeam,
AwayTeam = awayTeam,
UpdateDate = DateTime.Now
};
_dbContext.AddToMatches(newMatch);
_dbContext.SaveChanges();
UpdatedDate does not allow nulls. Make it a nullable column in your database.
And also in your EDMX as scheien mentioned in the comment.
Your schema in EF indicates that Null value is not allowed to be entered while adding/Inserting or Updating.
Make sure you are passing the correct non nullable value.
Also you can change the schema of the table and update the model, so that null can be entered.
Set a breakpoint here: awayTeam.UpdatedDate = DateTime.Now;
Then when you run it you can tell if it's pointing to the existing team or not.
I'm pretty certain that the issue is when you are trying to do an update. In that case you haven't detached your original object, instead you're trying to reassign. Give it a try to detach your existingAwayTeam, and then attach your matchData.AwayTeam, mark it as modified, and try saving it.

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