I have an MVC site that is connected to a database with multiple tables. Some of the tables have only 1000 or so records, so searching through those is fairly simple. However, one of the tables has over 2 million records, and I tried implementing the same searching method but it is far too slow. For the tables with less records, the search goes through every column looking for matches, but the table with a million records can only search through one column in an efficient manner. Essentially, what I want to do to fix this problem is to allow the user to search through the search results. So if I were to search by apple in the table with a million records, let's say that 100,000 records remain. Then, I could search by Gala on the remaining 100,000 records to narrow the results down even more. Is this possible? Or is there possibly a better way to search through this many records that I am not thinking of?
Controller
public ViewResult Index(string sortOrder, string currentFilter, string search, int? page, int? pageSize)
{
int pageIndex = 1;
pageIndex = page.HasValue? Convert.ToInt32(page) : 1;
int defaSize = (pageSize ?? 25);
ViewBag.psize = defaSize;
ViewBag.PageSize = new List<SelectListItem>()
{
new SelectListItem() { Value = "25",Text = "25"},
new SelectListItem() { Value = "50",Text = "50"},
new SelectListItem() { Value = "100",Text = "100"},
new SelectListItem() { Value = "1000",Text = "1000"},
};
ViewData["ControllerName"] = this.ToString();
ViewBag.CurrentSort = sortOrder;
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "pkDeviceUnderTest" : "";
if (search == null)
{
ViewBag.TotalCount = dbModel.DeviceUnderTests.Count();
}
else
{
var ld = dbModel.DeviceUnderTests.Where(x => x.nkDeviceUnderTest.Contains(search)||x.nkNotes.Contains(search)||x.FaultApplication.nkFaultApplication.Contains(search)||x.Firmware.nkFirmware.Contains(search)
||x.Hardware.nkHardware.Contains(search)||x.Location.nkLocation.Contains(search)||x.Power.nkPower.Contains(search)||x.nkHandleRating.ToString().Contains(search)||x.nkPreEventTime.ToString().Contains(search)).ToList()
.Count();
ViewBag.TotalCount = ld;
}
ViewBag.cf = currentFilter;
ViewBag.search = search;
Session["searchcircuit"] = search;
ViewBag.defaSize = defaSize;
var div = ViewBag.TotalCount / defaSize;
ViewBag.numofPages = ((ViewBag.defaSize - 1) + ViewBag.TotalCount) / ViewBag.defaSize;
ViewBag.index = pageIndex;
int pagenum = pageIndex;
var records = dbModel.DeviceUnderTests.Where(x => x.nkDeviceUnderTest.Contains(search) || x.nkNotes.Contains(search) || x.FaultApplication.nkFaultApplication.Contains(search) || x.Firmware.nkFirmware.Contains(search)
|| x.Hardware.nkHardware.Contains(search) || x.Location.nkLocation.Contains(search) || x.Power.nkPower.Contains(search) || x.nkHandleRating.ToString().Contains(search) ||search==null|| x.nkPreEventTime.ToString().Contains(search)).OrderBy(x => x.pkDeviceUnderTest).Skip(defaSize * (pagenum - 1)).Take(defaSize);
var result = new StaticPagedList<DeviceUnderTest>(records, pageIndex, defaSize, ViewBag.TotalCount);
return View(result);
}
View
#using (Html.BeginForm("Index", "Circuit", FormMethod.Get))
{
<input type="text" name="search" placeholder="Search ... " />
<input type="submit" name="submit" value="Search" class="btn btn-default" style="background-color:#337ab7;color:azure" />
}
Related
I am trying to create a ViewModel list with info from Receipt table and then if related info exist in Reason table retreive the last Description inserted (ReceiptId related) and add it (if not just pass null) to the ViewModel Receipt list (RejectDescription). Here's the DB model:
Database Model
I tryied many ways to achieve this, at the moment this is the code that partially works for me, i say partially because in RejectDescription saves the Reason.Description if it exist else just pass null and it's ok.
The main problem is when there's many Reason.Descriptions it doesn't return and save the last one inserted (the most recent, is the one that i am looking for). Here is my code:
[HttpPost]
public ActionResult ReceiptList(string Keyword)
{
using (SEPRETEntities DBC = new SEPRETEntities())
{
long UserId = (long)Session["Id"];
IEnumerable<Receipt> receipts = DBC.Receipts.Where(x => x.PersonId == UserId && x.Active == true).ToList();
#region Search
if (!string.IsNullOrEmpty(Keyword))
{
Keyword = Keyword.ToLower();
receipts = receipts.Where(x => x.Person.Name.ToLower().Contains(Keyword) ||
x.Person.MiddleName.ToLower().Contains(Keyword) ||
x.Person.LastName.ToLower().Contains(Keyword) ||
x.Person.Email.ToLower().Contains(Keyword) ||
x.Person.Enrollment.ToLower().Contains(Keyword) ||
x.Person.Career.ToLower().Contains(Keyword) ||
x.Payment.Name.ToLower().Contains(Keyword) ||
x.Payment.Price.ToString().ToLower().Contains(Keyword) ||
x.Method.Name.ToLower().Contains(Keyword) ||
x.Phase.Name.ToLower().Contains(Keyword) ||
x.TimeCreated.ToString().ToLower().Contains(Keyword) ||
x.Voucher.ToString().ToLower().Contains(Keyword)
);
}
#endregion
List<ReceiptVM> ReceiptList = receipts.Select(x => new ReceiptVM
{
Id = x.Id,
PaymentId = x.PaymentId,
Enrollment = x.Person.Enrollment,
Career = x.Person.Career,
PersonName = string.Concat(x.Person.Name, " ", x.Person.MiddleName, " ", x.Person.LastName),
Email = x.Person.Email,
PaymentName = x.Payment.Name,
MethodName = x.Method.Name,
Voucher = x.Voucher,
Image = x.Image,
PhaseId = x.Phase.Id,
PriceFormatted = x.Payment.Price.ToString("C"),
Active = x.Active,
TimeCreatedFormatted = x.TimeCreated.ToString(),
RejectDescription = x.Rejections.FirstOrDefault(y => y.ReasonId == y.Reason.Id)?.Reason.Description
}).ToList();
return PartialView("~/Views/Receipt/_SearchReceipt.cshtml", ReceiptList);
}
}
For you information i am kinda newbie working on C# and ASP.NET MVC.Not sure if there's a better way to achieve this or something, any advice or tip is pretty appreciated.
Thank you and sorry for my bad english
You have to order reject reasons by Id which will fetch recent reason like below :
RejectDescription = x.Rejections.OrderByDescending(x=>x.Reason.Id).FirstOrDefault(y => y.ReasonId == y.Reason.Id)?.Reason.Description
Or you can use LastOrDefault to get most recent one like below:
RejectDescription = x.Rejections.LastOrDefault(y => y.ReasonId == y.Reason.Id)?.Reason.Description
List<ReceiptVM> ReceiptList = receipts.Select(x => new ReceiptVM
{
Id = x.Id,
PaymentId = x.PaymentId,
Enrollment = x.Person.Enrollment,
Career = x.Person.Career,
PersonName = string.Concat(x.Person.Name, " ", x.Person.MiddleName, " ", x.Person.LastName),
Email = x.Person.Email,
PaymentName = x.Payment.Name,
MethodName = x.Method.Name,
Voucher = x.Voucher,
Image = x.Image,
PhaseId = x.Phase.Id,
PriceFormatted = x.Payment.Price.ToString("C"),
Active = x.Active,
TimeCreatedFormatted = x.TimeCreated.ToString(),
RejectDescription = x.Rejections.OrderByDescending(x=>x.Reason.Id).FirstOrDefault(y => y.ReasonId == y.Reason.Id)?.Reason.Description
}).ToList();
I am working with C# and Linq and what I intend is to show a series of data that I add to a list with the currency format.
Data in SQL Server (RealEjecutado <-- is what i want to convert)
100000.00
I want it to be displayed like this:
$100,000.00
My code
List<Dashboard> list = new List<Dashboard>();
using (Web_INCAEntities dc = new Web_INCAEntities())
{
var v = (from a in dc.TBL_PBI
select new Dashboard
{
id = a.id,
RealEjecutado = a.RealEjecutado,
PlanVigente = a.PlanVigente,
Reprogramacion = a.Reprogramacion
});
list = v.ToList();
}
return View("../Dashboard/PontoHorizonte/Graficas", list);
Markup:
#grid.GetHtml(
tableStyle: "fl-table",
htmlAttributes: new { id = "tablaadmin" },
columns: grid.Columns(
grid.Column(header: "Real Ejecutado", format: #<text><div class="" data-id="#item.id" data-propertyname="RealEjecutado" id="" ><p id="userinput">#item.RealEjecutado</p></div></text>),
grid.Column(header: "Plan Vigente", format:#<text><div class="" data-id="#item.id" data-propertyname="PlanVigente">#item.PlanVigente</div></text>),
grid.Column(header: "Proyección INCA", format:#<text><div class="" data-id="#item.id" data-propertyname="Reprogramacion">#item.Reprogramacion</div></text>)
)
)
I have not found on the web something that works for me, that is why I ask your help to solve this, thanks in advance
Building off of Brandon's answer, you can do
i.ToString("C", CultureInfo.CreateSpecificCulture("en-US"))
to get the dollar format like so
using (Web_INCAEntities dc = new Web_INCAEntities())
{
var v = (from a in dc.TBL_PBI
select new Dashboard
{
id = a.id,
RealEjecutado = a.RealEjecutado,
PlanVigente = a.PlanVigente,
Reprogramacion = a.Reprogramacion
});
list = v.ToList().Select(x => new Dashboard
{
id = x.id,
RealEjecutado = Decimal.TryParse(x.RealEjecutado, out decimal i) ? i.ToString("C", CultureInfo.CreateSpecificCulture("en-US")) : x.RealEjecutado,
PlanVigente = x.PlanVigente,
Reprogramacion = x.Reprogramacion
}).ToList();
}
return View("../Dashboard/PontoHorizonte/Graficas", list);
This is possibly not the most efficient way to accomplish this, but it should work given what you said in the question. The second select is because I believe LinqToEntities will complain about the function usages. This will try to parse the value to Decimal. If successful, it will then use the currency string converter. If it fails, it will just use the bare value of RealEjecutado.
using (Web_INCAEntities dc = new Web_INCAEntities())
{
var v = (from a in dc.TBL_PBI
select new Dashboard
{
id = a.id,
RealEjecutado = a.RealEjecutado,
PlanVigente = a.PlanVigente,
Reprogramacion = a.Reprogramacion
});
list = v.ToList().Select(x => new Dashboard
{
id = x.id,
RealEjecutado = Decimal.TryParse(x.RealEjecutado, out decimal i) ? i.ToString("C") : x.RealEjecutado,
PlanVigente = x.PlanVigente,
Reprogramacion = x.Reprogramacion
}).ToList();
}
return View("../Dashboard/PontoHorizonte/Graficas", list);
public static string DecimalToFormattedStringCurrency(decimal? decimalValue, string decimalFormatter = null)
{
if (String.IsNullOrWhiteSpace(decimalFormatter))
{
decimalFormatter = "{0:C0}";
}
return decimalValue.HasValue ? String.Format(decimalFormatter, decimalValue) : null;
}
I have mission system in MVC. and I give a same mission or diffrent mission for a lot of users. and I show title, description, start date , finish date and who are/is in mission. These are showing view page in grid.mvc. but when I login I can see every mission. I dont want to every mission I just want to see only my mission. of course other users see their missions.
in my controller, I split names.
this is my Codes,
Controller:
TicketDbContext db = new TicketDbContext();
public ActionResult Index()
{
var result = db.Missions.OrderByDescending(x=>x.GivenDate).ToList();
string ad = string.Empty;
foreach (var item in result)
{
if (!string.IsNullOrEmpty(item.GivenUsers))
{
string[] Ids = item.GivenUsers.Split(',');
for (int i = 0; i < Ids.Length; i++)
{
int id = Convert.ToInt32(Ids[i]);
item.GivenUsers += db.Users.FirstOrDefault(x => x.Id == id).Name+ " " + db.Users.FirstOrDefault(x => x.Id == id).Surname+ ",";
}
}
}
return View(result);
}
ScreenShot of my Grid
firstly, I recommend that you keep the assigned users ids in separated table
This is can handle your case..
var currentUserId = "";// set current userId from Where are you holding (session,identity etc..)
var result = db.Missions.OrderByDescending(x=>x.GivenDate).ToList();
result = result.Where(x=> x.GivenUsers.Split(',').Contains(currentUserId));
foreach (var item in result)
{
if (!string.IsNullOrEmpty(item.GivenUsers))
{
int[] Ids = Array.ConvertAll(item.GivenUsers.Split(','),
delegate(string s) { return int.Parse(s); }) ;
string[] names = db.Users.Where(u => Ids.Contains(u.Id)).Select(u => u.Name+ " " + u.Surname).ToArray();
item.GivenUsers = string.Join(",",names);
}
}
I have a controller with two actions. One action is simply a post that updates the database. The other is the view model. However in my action that updates the database I return it back to the original view with the view model.
public ActionResult ManageMxieUsers()
{
var model = from el in aphdb.view_EmployeeList
join p in aphdb.MxieCallRevPermissions on el.Id equals p.AspNetUserUserID into gj
from sub in gj.DefaultIfEmpty()
select new vm_ManageMxieUsers { Id = el.Id, UserName = el.UserName, PermissionLevel = sub.PermissionLevel, MxieCallRevPermCustomList = aphdb.MxieCallRevPermCustomList.Where(w => w.PermissionID == sub.PermissionID ) };
ViewBag.EmployeeList = aphdb.view_EmployeeList.OrderBy(o => o.UserName);
return View(model);
}
This part works perfectly.
Here is my post method:
[HttpPost]
public ActionResult ManageMxieUsers(string userID, Int16 permissionLevel, List<string> customUserList)
{
var UserToEdit = aphdb.MxieCallRevPermissions.Where(w => w.AspNetUserUserID == userID).FirstOrDefault();
if (UserToEdit == null)
{
MxieCallRevPermissions addPerm = new MxieCallRevPermissions();
addPerm.AspNetUserUserID = userID;
addPerm.PermissionLevel = permissionLevel;
aphdb.MxieCallRevPermissions.Add(addPerm);
aphdb.SaveChanges();
if (permissionLevel == 3)
{
foreach (var id in customUserList)
{
MxieCallRevPermCustomList list = new MxieCallRevPermCustomList();
list.PermissionID = addPerm.PermissionID;
list.AspNetUserID = id;
aphdb.MxieCallRevPermCustomList.Add(list);
}
}
aphdb.SaveChanges();
#ViewBag.Success = true;
}
else
{
UserToEdit.PermissionLevel = permissionLevel;
aphdb.SaveChanges();
if (permissionLevel == 3)
{
// Remove old custom list
var customList = aphdb.MxieCallRevPermCustomList.Where(w => w.PermissionID == UserToEdit.PermissionID).ToList();
aphdb.MxieCallRevPermCustomList.RemoveRange(customList);
aphdb.SaveChanges();
foreach (var id in customUserList)
{
MxieCallRevPermCustomList list = new MxieCallRevPermCustomList();
list.PermissionID = UserToEdit.PermissionID;
list.AspNetUserID = id;
aphdb.MxieCallRevPermCustomList.Add(list);
}
aphdb.SaveChanges();
}
else
{
// Remove old custom list
var customList = aphdb.MxieCallRevPermCustomList.Where(w => w.PermissionID == UserToEdit.PermissionID).ToList();
aphdb.MxieCallRevPermCustomList.RemoveRange(customList);
aphdb.SaveChanges();
}
aphdb.SaveChanges();
#ViewBag.Success = true;
}
//aphdb.SaveChangesAsync(); // Testing ot make sure SaveChanges fired before this code.
var model = from el in aphdb.view_EmployeeList
join p in aphdb.MxieCallRevPermissions on el.Id equals p.AspNetUserUserID into gj
from sub in gj.DefaultIfEmpty()
select new vm_ManageMxieUsers { Id = el.Id, UserName = el.UserName, PermissionLevel = sub.PermissionLevel, MxieCallRevPermCustomList = aphdb.MxieCallRevPermCustomList.Where(w => w.PermissionID == sub.PermissionID) };
ViewBag.EmployeeList = aphdb.view_EmployeeList.OrderBy(o => o.UserName);
//return RedirectToAction("ManageMxieUsers"); // WORKS
return View("ManageMxieUsers", model); // ERROR NULL on AspNetUser
}
Now when I simply return View("ManageMxieUsers", model); The error comes back saying AspNetUsers is null. However when I reload this page it works just fine. When I return RedirectToAction("ManageMxieUsers"); I get no error, but of course I lose my ViewBag.
My only guess is that aphdb.SaveChanges() is not reflected instantly? Even tho I can perform a SQL query to the database and see the data has indeed been added/updated. As is referenced by reloading the current page.
So I'm at a loss as to why during this post method my view model portion comes up with null entries, but when I reload it they are populated normally.
I'm fine with leaving it as RedirectToAction, but would like to maybe understand better as to why this behavior is making it work.
Here is the view portion that breaks
<tbody>
#{ int? permissionLevel; }
#foreach (var user in Model)
{
permissionLevel = user.PermissionLevel ?? 4;
<tr>
<td>#user.Id</td>
<td>
#user.UserName
</td>
<td>#permissionLevel</td>
<td>
#foreach (var customUser in user.MxieCallRevPermCustomList)
{
#("[" + customUser.AspNetUsers.FirstName + " " + customUser.AspNetUsers.Lastname + "] ")
}
</td>
</tr>
}
</tbody>
the customUser.AspNetUsers = null when I use return View("ManageMxieUsers", model); in my [HttpPost] method.
I have a mvcjqgrid:
#(Html.Grid("dataGrid")
.SetJsonReader(new MvcJqGrid.DataReaders.JsonReader { Id = "Id", RepeatItems = false })
.SetRequestType(RequestType.Post)
.AddColumn(new Column("Name").SetLabel("Name").SetSearch(true))
.AddColumn(new Column("Email").SetLabel("E-Mail").SetSearch(true).SetFormatter(Formatters.Email))
.AddColumn(new Column("Phone").SetLabel("Phone").SetSearch(true))
.SetSearchToolbar(true)
.SetUrl(Url.Action("GetData", "Controller"))
.SetSearchOnEnter(false)
.SetRowNum(10)
.SetRowList(new[] { 10, 15, 20, 50 })
.SetViewRecords(true)
.SetPager("pager"))
and controller:
public ActionResult GetData()
{
return View(new myEntity[0]);
}
[HttpPost]
public JsonResult GetData(GridSettings gridSettings)
{
int totalRecords = DataHelper.GetCount();
var data = DataHelper.GetData(gridSettings);
var jsonData = new
{
total = totalRecords / gridSettings.PageSize + 1,
page = gridSettings.PageIndex,
records = totalRecords,
rows = data
};
return Json(jsonData);
}
EDIT:
so my question is how can i store GridSettings in session in right way, i need every time user backs to this page, page should be the same as when he left?
If i do:
Session["settings"] = gridSettings;
i need some way to compare stored gridSettings with the one passed to action.
Why don't you use the Http Cache for this case? We can write a caching provider, and Http Cache is one implementation for this. So in the future you can extend more providers for it.
The answer is to recreate Grid:
#{
var setting = Session["settings"] as GridSettings;
}
#(Html.Grid("dataGrid")
.SetJsonReader(new MvcJqGrid.DataReaders.JsonReader { Id = "Id", RepeatItems = false })
.SetRequestType(RequestType.Post)
.AddColumn(new Column("Name").SetLabel("Name").SetSearch(true))
.AddColumn(new Column("Email").SetLabel("E-Mail").SetSearch(true).SetFormatter(Formatters.Email))
.AddColumn(new Column("Phone").SetLabel("Phone").SetSearch(true))
.SetSearchToolbar(true)
.SetUrl(Url.Action("GetData", "Controller"))
.SetSearchOnEnter(false)
.SetRowNum(setting != null?setting.PageSize : 10)
.SetPage(setting != null?setting.PageIndex : 1);
.SetSortName(setting != null?setting.SortColumn : "");
.SetRowList(new[] { 10, 15, 20, 50 })
.SetViewRecords(true)
.SetPager("pager"))