Loading data from one table via another - entity-framework

I am trying to load each staff members holiday record but I am unable to populate holiday records.
Here is the code from my controller:
public class HomeController : Controller
{
private HRModel.db_HRSystemModel db = new HRModel.db_HRSystemModel();
public IActionResult Index()
{
var staff = (from s in db.Hrstaffpersonaldetails
where s.Firstname == "Ian"
select s).ToList();
return View(staff);
}
}
and here is the code from my view:
<div>
<h4>Staff</h4>
<hr />
#foreach (var s in Model)
{
<dl class="dl-horizontal">
<dt>
First Name:
</dt>
<dd>
#s.Firstname
</dd>
<dt>
Surname:
</dt>
<dd>
#s.Surname
</dd>
<dt>
Username:
</dt>
<dd>
#s.Username
</dd>
#foreach (var hr in s.Hrstaffholidayrecords)
{
<dt>
date:
</dt>
<dd>
#hr.Startdate
</dd>
}
</dl>
}
</div>
When I currently run what I have (on .NET Core 1.1), holiday records returns 0 records from the database. I'm not really sure if I'm missing something or I'm doing it wrong.
Thanks,
Ian

Change the controller code to this should work.
public class HomeController : Controller
{
private HRModel.db_HRSystemModel db = new HRModel.db_HRSystemModel();
public IActionResult Index()
{
var staff = db.Hrstaffpersonaldetails
.Include(s => s.Hrstaffholidayrecords)
.Where(s => s.Firstname == "Ian")
.ToList();
return View(staff);
}
}
This is called eager loading. Read more at https://learn.microsoft.com/en-us/ef/core/querying/related-data
Besides, the db context object is supposed to be injected into controller using DI. Read more at https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/dependency-injection

looks like lazy loading, but not sure, not that strong at this) you should read smth about fluent API

Related

Using drop-down as filter with Blazorise.DataGrid

The Blazorise DataGrid supports textbox filters.
I would like to use a drop-down component to allow filtering by specific values. What do I need to do to make the grid react to the change of the Select value?
Code Example
#page "/ItemList"
#using DataAccessLibrary
#using Blazorise
#using Blazorise.DataGrid
#inject IItemList _items;
<div align="center">
<h3>Item List</h3>
</div>
#if (ItemListItems is null)
{
<p>Loading...</p>
}
else
{
<DataGrid Data="#ItemListItems" TItem="ItemListItem" PageSize="20" ShowPager="true" Filterable="true" Striped="true" Narrow="true" #bind-SelectedRow="#selectedItem">
<EmptyTemplate>
<div class="box">
No items were found!
</div>
</EmptyTemplate>
<DataGridColumns>
<DataGridCommandColumn TItem="ItemListItem" Caption="Action" EditCommandAllowed="true">
<EditCommandTemplate>
<Button Color="Color.Primary" Clicked="#context.Clicked">Edit</Button>
</EditCommandTemplate>
</DataGridCommandColumn>
<DataGridNumericColumn TItem="ItemListItem" Field="#nameof(ItemListItem.ItemID)" Caption="Item ID" Sortable="true" TextAlignment="TextAlignment.Right">
<DisplayTemplate>
#(context.ItemID)
</DisplayTemplate>
</DataGridNumericColumn>
<DataGridSelectColumn TItem="ItemListItem" Field="#nameof(ItemListItem.TypeShortDesc)" Caption="Item Type" Sortable="true">
// This filter should replace the default textbox with a dropdown listing only specific values
<FilterTemplate>
<Select TValue="string" #bind-SelectedValue="#ItemTypeFilter">
#foreach (string type in ItemTypeList)
{
<SelectItem Value="#type">#type</SelectItem>
}
</Select>
</FilterTemplate>
</DataGridSelectColumn>
<DataGridSelectColumn TItem="ItemListItem" Field="#nameof(ItemListItem.Description)" Caption="Item Description" Sortable="true" TextAlignment="TextAlignment.Left" />
<DataGridSelectColumn TItem="ItemListItem" Field="#nameof(ItemListItem.StatusShortDesc)" Caption="Status" Sortable="true">
<FilterTemplate>
// This filter should replace the default textbox with a dropdown listing only specific values
<Select TValue="string" #bind-SelectedValue="#ItemStatusFilter">
#foreach(string status in ItemStatusList)
{
<SelectItem Value="#status">#status</SelectItem>
}
</Select>
</FilterTemplate>
</DataGridSelectColumn>
<DataGridNumericColumn TItem="ItemListItem" Field="#nameof(ItemListItem.ItemPrice)" Caption="Amount" Sortable="false" TextAlignment="TextAlignment.Right" DisplayFormat="{0:C}" />
</DataGridColumns>
</DataGrid>
}
#code {
private List<ItemListItem> ItemListItems;
private ItemListItem selectedItem;
private List<string> ItemStatusList;
private string ItemStatusFilter;
private List<string> ItemTypeList;
private string ItemTypeFilter;
protected override async Task OnInitializedAsync()
{
ItemListItems = await _items.GetItems();
ItemTypeList = await _items.GetItemTypes();
ItemStatusList = await _items.GetItemStatuses();
}
}

How to access DbContext in viewmodel with AspNetCore 2.1?

Using DropDown input fields is very common when you want to display a description while saving an ID in your database.
If I consider my Person model, I have PersonViewModel which has a SelectList used to display a list of possible Job Descriptions
public SelectList SelectJobDescription
{
get
{
MyDbContext _context = new MyDbContext();
var result = new SelectList(_context.Keywords
.Where(k => k.Name == ".JobDescription")
.OrderBy(r => r.Valuename), "Valuecode", "Valuename");
_context.Dispose();
return result;
}
}
and I use this SelectList in my create/edit views like this:
<div class="form-group row">
<label asp-for="JobDescription" class="col-sm-4 col-form-label"></label>
<div class="col-sm-8">
<select asp-for="JobDescription" asp-items="#Model.SelectJobDescription" class="form-control">
<option>Select a Job Description</option>
</select>
</div>
Is it correct to use _context this way in the ViewModel or there is some other way to do the same thing better (maybe DI?)
Set the property after instatiating the view model (or during initialization), for example:
var vm = new YourViewModel()
{
SelectJobDescription = new SelectList( _context... ),
};
return View( vm );

Html.DropDownListFor default selected value does not work

I have read hundreds of posts about this problem and I still can't find a solution.
Please help with this horrible mistery;
I would like to have different default values in my DropDownListFor. The "PartialViewList1 exists out of 4 items.
I want the DropDownListFor to select the id of the current item. (item.id)
But because of testing purposes I just filled in "3". And even that doesn't work.
The Models are filled correctly, I am able to add more code of the controller but that wouldn't add much. But please ask if you want me to.
And yes I know that it is better to make the SelectList in the controller, but first I want to make it work.
View:
#foreach (var item in Model.PartialViewList1)
{
<tr>
<td>Plaats: </td>
<td>#item.PlaceNumber</td>
<td>
#Html.DropDownListFor(x => x.PartialView.Id, new SelectList(Model.PartialViewList2, "Id", "Name", 3),
new { onchange = "this.form.submit();" })</td>
</tr>
}
Screen shot of the users view
I hope that maybe someone can use this for his or her problem.
With Stephen Mueke I have found the solution. The problem is that if "x => x.PartialView.Id" already has a value then the default value : "3" will be overriden by the Id.
And you can't generate multiple DropDownlistFor's while binding them to the same property.
My solution on my problem:
View:
#using (Html.BeginForm("_PartialSettingsDropDownList1", "Home")){
<table>
#for (int i = 0; i < Model.maxNumberOfViews; i++)
{
<tr>
<td>
Plaats #(i+1)
</td>
<td>
#Html.DropDownListFor(x => Model.PartialViewList[i].ID, new SelectList(Model.PartialViewList, "Id", "Name", Model.PartialViewList[i].ID), "select")
</td>
</tr>
}
</table>
#Html.HiddenFor(x => x.maxNumberOfViews)
<input class="submit" type="submit" value="Submit" />}
Controller:
[HttpGet]
public PartialViewResult _PartialSettingsDropDownList1()
{
PartialScreenViewModel viewModel = new PartialScreenViewModel();
viewModel.PartialViewList = homeModel.AllBoxViews(databaseRepository.PartialViews);
viewModel.maxNumberOfViews = viewModel.PartialViewList.Count();
return PartialView(viewModel);
}
[HttpPost]
public RedirectResult _PartialSettingsDropDownList1(PartialScreenViewModel viewModel)
{
for (int i = 0; i < viewModel.maxNumberOfViews; i++)
{
PartialView viewOnScreen = databaseRepository.PartialViews.FirstOrDefault(x => x.ID == viewModel.PartialViewList[i].ID);
databaseRepository.UpdatePartialView(viewOnScreen, i+1);
}
return new RedirectResult("Settings");
}
Model:
public List<PartialView> AllBoxViews(IEnumerable<PartialView> allViews)
{
List<PartialView> OnlyBoxViews = new List<PartialView>();
foreach (var item in allViews.Where(item => item.Type.Equals("box")))
{
OnlyBoxViews.Add(item);
}
return OnlyBoxViews;
}
ViewModel:
public class PartialScreenViewModel
{
public List<PartialView> PartialViewList { get; set; }
public int maxNumberOfViews { get; set; }
}
Result on screen: screenshot

asp.net core pass linq lambda to view

I'm using asp.net core, when I pass linq lambda query to view I get this error:
An unhandled exception occurred while processing the request.
InvalidOperationException: The model item passed into
the ViewDataDictionary is of type 'System.Collections.Generic.List`1[<>f__AnonymousType7`1[System.Int64]]',
but this ViewDataDictionary instance
requires a model item of type 'System.Collections.Generic.IEnumerable`1[HRMS.Salaries]'.
This my query:
public async Task<IActionResult> Index()
{
var salary = (from salaries in _context.Salaries select new { salaries.Id });
return View(await salary.ToListAsync());
}
and in the view I use:
#model IEnumerable<HRMS.Salaries>
#foreach (var item in Model)
{
<tr>
<td>#item.Id</td>
</tr>
}
Is there a reason you create an object in your query? If no, try this instead :
public async Task<IActionResult> Index()
{
var salary = (from salaries in _context.Salaries
select salaries.Id
);
return View(await salary.ToListAsync());
}
Then in your view :
#model IEnumerable<int>
#foreach (var item in Model)
{
<tr>
<td>#item</td>
</tr>
}
Else, if you need the object, use in your query :
public async Task<IActionResult> Index()
{
var salary = (from salaries in _context.Salaries
select salaries
);
return View(await salary.ToListAsync());
}
And keep your view the same :
#model IEnumerable<HRMS.Salaries>
#foreach (var item in Model)
{
<tr>
<td>#item.Id</td>
</tr>
}
EDIT : If you want to pass multiple fields to your View, it's better to use a new object. For this, create a class (for exemple, SalaryDetailsViewModel) with the required fields. Then in your controller :
public async Task<IActionResult> Index()
{
var salary = (from salaries in _context.Salaries
select new SalaryDetailsViewModel {
Id = salaries.Id,
Amount = salaries.Amount,
Date = salaries.Date,
JobTitle = salaries.JobTitle.Name }
);
return View(await salary.ToListAsync());
}
Then adjust your View to call the different fields of your custom object, for display purpose, for example :
#model IEnumerable<SalaryDetailsViewModel>
#foreach (var item in Model)
{
<tr>
<td>#item.Id</td>
<td>#item.Amount</td>
<td>#item.Date</td>
<td>#item.JobTitle</td>
</tr>
}

single page with multiple partials and forms

What is the best practice for this:
I have a list of partials on one page. Each page has a form on it to save the data of the partial.
I do this like this on the main page:
#foreach (var taak in Model)
{
#Html.Action("DetailTaak", "Checklist", new { trouwTaakId = taak.Id })
}
Then the controller is this (where the data is filled):
public ActionResult DetailTaak(int trouwTaakId)
{
DTO.Trouw.TrouwTaak viewModel;
viewModel = _themaService.GetTrouwTaakByTrouwTaakId(trouwTaakId);
return View(viewModel);
}
The page gets build and the list is completed. Now when I want to save a partial using this code:
#using (Html.BeginForm("DetailTaak", "Checklist", FormMethod.Post, new { #class = "form-horizontal col-md-12", role = "form", id = #Model.Id }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
...
}
It works but what do I do so the mainpage doesn't have to be reloaded ? This is the post code:
[HttpPost]
public ActionResult DetailTaak(DTO.Trouw.TrouwTaak model)
{
if (ModelState.IsValid)
{
ViewBag.SaveSuccess = "Je instellingen zijn opgeslagen";
}
return View("DetailTaak", model);
}
With this post code I go to the DetailTaak page instead of staying on the main page and just updating the partial.
I am doing something wrong but I don't know which way to go.
kind regards