I wonder how to refactor a piece of code like this. It is a method in controller and I'm passing 2 SelectLists to Html via ViewBag.
public async Task<IActionResult> Index()
{
var teacherInfo = context.Teachers.OrderBy(x => x.Id);
IEnumerable<SelectListItem> selectList = from s in teacherInfo
select new SelectListItem
{
Value = s.Id.ToString(),
Text = s.FirstName + " " + s.LastName.ToString()
};
ViewBag.TeacherId = new SelectList(selectList, "Value", "Text");
var studentInfo = context.Students.OrderBy(x => x.Id);
IEnumerable<SelectListItem> selectListStudents = from s in studentInfo
select new SelectListItem
{
Value = s.Id.ToString(),
Text = s.FirstName + " " + s.LastName.ToString()
};
ViewBag.StudentId = new SelectList(selectListStudents, "Value", "Text");
return View();
}
I've tried this but it seems i can't use generic like this because T has no definition for Id and other fields:
private async Task<IEnumerable<SelectListItem>> GetSelectListItem<T>(IOrderedQueryable<T> dbData)
{
IEnumerable<SelectListItem> selectList = from s in dbData
select new SelectListItem
{
Value = s.Id.ToString(),
Text = s.FirstName + " " + s.LastName.ToString()
};
return selectList;
}
Any help would be appreciated
You need to constraint the T to an interface which contains those properties
internal interface IListItem
{
int Id { get; }
string FirstName { get; }
string LastName { get; }
}
Then make your student and teacher implement this interface.
You can create a generic class for Students and Teachers classes to inherit.
public class BaseClassForSelectItems
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Students: BaseClassForSelectItems
{
}
public class Teachers: BaseClassForSelectItems
{
}
public async Task<IActionResult> Index()
{
var teacherInfo = context.Teachers.OrderBy(x => x.Id);
IEnumerable<SelectListItem> selectList = await GetSelectListItem(teacherInfo);
ViewBag.TeacherId = new SelectList(selectList, "Value", "Text");
var studentInfo = context.Students.OrderBy(x => x.Id);
IEnumerable<SelectListItem> selectListStudents = await GetSelectListItem(studentInfo);
ViewBag.StudentId = new SelectList(selectListStudents, "Value", "Text");
return View();
}
private async Task<IEnumerable<SelectListItem>> GetSelectListItem<T>(IOrderedQueryable<T> dbData) where T : BaseClassForSelectItems
{
IEnumerable<SelectListItem> selectList = from s in dbData
select new SelectListItem
{
Value = s.Id.ToString(),
Text = s.FirstName + " " + s.LastName.ToString()
};
return selectList;
}
Related
Its crashing on my CardDetailsView,the Details IActionResult in my CardsController
CardDetailsView-
public class CardDetailsViewModel
{
public string Id { get; set; }
public string Title { get; set; }
public string ImageUrl { get; set; }
public string Destination { get; set; }
public string Model { get; set; }
public string SNumber { get; set; }
public string QNumber { get; set; }
}
CardDetailsView-
#model CardDetailsViewModel
#{ ViewBag.Title = "Details"; }
Card Details
Title
Destination
Model
SNumber
QNumber
Details
CardsController-
public class CardsController : Controller
{
public readonly DigitalCardsDbContext data;
public CardsController(DigitalCardsDbContext data)
{
this.data = data;
}
public IActionResult Add() => View();
[HttpPost]
public IActionResult Add(CardAddViewModel card)
{
if(!ModelState.IsValid)
{
return View(card);
}
var cardd = new Card
{
Title = card.Title,
ImageUrl = card.ImageUrl,
Destination = card.Destination,
Receiver = card.Receiver,
Model = card.Model,
UserFullName = card.UserFullName,
SNumber = card.SNumber,
QNumber = card.QNumber,
PublicView = card.PublicView
};
this.data.Cards.Add(cardd);
this.data.SaveChanges();
return RedirectToAction("All","Cards");
}
public IActionResult All()
{
var cards = this.data.Cards
.Where(c => c.PublicView == true).ToList();
var usern = User.Identity.Name;
if(usern!=this.User.Identity.Name)
{
return BadRequest();
}
var cardsl = cards
.Select(c => new CardAllViewModel
{
Id = c.Id,
Title = c.Title,
ImageUrl = c.ImageUrl,
Destination = c.Destination,
SNumber = c.SNumber,
QNumber = c.QNumber
})
.ToList();
return View(cardsl);
}
public IActionResult Details(string cardId)
{
var card = this.data.Cards.Where(c => c.Id == cardId)
.Select(c => new CardDetailsViewModel
{
Id = c.Id,
ImageUrl = c.ImageUrl,
Title = c.Title,
Destination = c.Destination,
Model = c.Model,
SNumber = c.SNumber,
QNumber = c.QNumber
})
.SingleOrDefault();
return View(card);
}
The code is crashing on my View,on every div class,Title,ImageUrl,Etc.The exception is Microsoft.AspNetCore.Mvc.Razor.RazorPage.Model.get returned null.
Here is snap-Error
Please verify the value in the image [url] it looks like that could be the reason.
I have two DbSets:
public DbSet<Reports.Models.Application> Application { get; set; }
public DbSet<Reports.Models.Category> Category { get; set; }
In the controller, I'm creating two List<SelectListItem>s:
var applications = _context.Application
.Select(listItem => new SelectListItem
{
Value = listItem.ID,
Text = listItem.Name
}
).ToList();
var categories = _context.Category
.Select(listItem => new SelectListItem
{
Value = listItem.ID,
Text = listItem.Name
}
).ToList();
I'd like to refactor this into a single, private method:
private List<SelectListItem> SelectList<T>(bool blankListItem = false)
{
var selectListItems = _context.<T> <------ doesn't compile
.Select(listItem => new SelectListItem
{
Value = listItem.ID,
Text = listItem.Name
}
).ToList();
if (blankListItem)
selectListItems.Insert(0, (new SelectListItem { Text = $"Choose {{T.ToString}}", Value = "" }));
return selectListItems;
}
And call it twice:
var applications = SelectList<Application>();
var categories = SelectList<Category>();
or
var applications = SelectList<Application>(true); // add "choose"
var categories = SelectList<Category>(true); // add "choose"
What's the right way to define the _context.<T> part? Perhaps this should be an extension method of the DbSet instead?
Maybe you can have your dbsets inherit a base class. which would be representing the generic type T.
Something like;
public class BaseClassForDbSets
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Application : BaseClassForDbSets
{
}
public class Category : BaseClassForDbSets
{
}
and then your private method;
private IEnumerable<SelectListItem> GetSelectList<T>(IEnumerable<T> dataSource, bool blankListItem = false) where T : BaseClassForDbSets
{
var selectListItems = dataSource
.Select(listItem => new SelectListItem
{
Value = listItem.Id.ToString(),
Text = listItem.Name
}
).ToList();
if (blankListItem)
selectListItems.Insert(0, (new SelectListItem { Text = $"Choose {nameof(T)}", Value = "" }));
return selectListItems;
}
Then you would call it like;
var applicationCollection = GetSelectList(_context.Application);
var categoryCollection = GetSelectList(_context.Category);
Do note - not tested
My solution uses a different approach, but same result.
Start with an interface:
public interface IBaseSelectItem
{
int Id { get; set; }
string Name { get; set; }
}
Have your entities (Application and Category) implement the interface:
public partial class Category : IBaseSelectItem
{
public int Id { get; set; }
public string Name { get; set; }
}
Create an extension on DbSet:
public static IList<SelectListItem> AsSelectList<T>(this DbSet<T> dbSet, bool useChooseValueOption) where T : class, IBaseSelectItem
{
var selectList = dbSet
.Select(c => new SelectListItem { Value = c.Id.ToString(), Text = c.Name })
.ToList();
if (useChooseValueOption)
selectList.Insert(0, new SelectListItem { Value = "0", Text = "-Choose Value-" });
return selectList;
}
Then use like this:
var categoriesSelectList = _dbContext.Categories.AsSelectList();
I'm developing an ASP.NET MVC 5 application, with C# and .NET Framework 4.6.1.
I have this View:
#model MyProject.Web.API.Models.AggregationLevelConfViewModel
[...]
#Html.DropDownListFor(m => m.Configurations[0].HelperCodeType, (SelectList)Model.HelperCodeTypeItems, new { id = "Configurations[0].HelperCodeType" })
The ViewModel is:
public class AggregationLevelConfViewModel
{
private readonly List<GenericIdNameType> codeTypes;
private readonly List<GenericIdNameType> helperCodeTypes;
public IEnumerable<SelectListItem> CodeTypeItems
{
get { return new SelectList(codeTypes, "Id", "Name"); }
}
public IEnumerable<SelectListItem> HelperCodeTypeItems
{
get { return new SelectList(helperCodeTypes, "Id", "Name"); }
}
public int ProductionOrderId { get; set; }
public string ProductionOrderName { get; set; }
public IList<Models.AggregationLevelConfiguration> Configurations { get; set; }
public AggregationLevelConfViewModel()
{
// Load CodeTypes to show it as a DropDownList
byte[] values = (byte[])Enum.GetValues(typeof(CodeTypes));
codeTypes = new List<GenericIdNameType>();
helperCodeTypes = new List<GenericIdNameType>();
for (int i = 0; i < values.Length; i++)
{
GenericIdNameType cType = new GenericIdNameType()
{
Id = values[i].ToString(),
Name = EnumHelper.GetDescription((CodeTypes)values[i])
};
if (((CodeTypes)values[i]) != CodeTypes.NotUsed)
codeTypes.Add(cType);
helperCodeTypes.Add(cType);
}
}
}
And Models.AggregationLevelConfiguration is:
public class AggregationLevelConfiguration
{
public byte AggregationLevelConfigurationId { get; set; }
public int ProductionOrderId { get; set; }
public string Name { get; set; }
public byte CodeType { get; set; }
public byte HelperCodeType { get; set; }
public int PkgRatio { get; set; }
public int RemainingCodes { get; set; }
}
I need to set selected value in these properties:
public IEnumerable<SelectListItem> CodeTypeItems
{
get { return new SelectList(codeTypes, "Id", "Name"); }
}
public IEnumerable<SelectListItem> HelperCodeTypeItems
{
get { return new SelectList(helperCodeTypes, "Id", "Name"); }
}
But I can't set it in new SelectList(codeTypes, "Id", "Name"); or new SelectList(helperCodeTypes, "Id", "Name"); because the selected value are in Configurations array: fields AggregationLevelConfiguration.CodeType and AggregationLevelConfiguration.HelperCodeType.
I think I have to set selected value in the View, but I don't know how to do it.
How can I set the selected values?
Unfortunately #Html.DropDownListFor() behaves a little differently than other helpers when rendering controls in a loop. This has been previously reported as an issue on CodePlex (not sure if its a bug or just a limitation)
The are 2 option to solve this to ensure the correct option is selected based on the model property
Option 1 (using an EditorTemplate)
Create a custom EditorTemplate for the type in the collection. Create a partial in /Views/Shared/EditorTemplates/AggregationLevelConfiguration.cshtml (note the name must match the name of the type
#model yourAssembly.AggregationLevelConfiguration
#Html.DropDownListFor(m => m.HelperCodeType, (SelectList)ViewData["CodeTypeItems"])
.... // other properties of AggregationLevelConfiguration
and then in the main view, pass the SelectList to the EditorTemplate as additionalViewData
#using (Html.BeginForm())
{
...
#Html.EditorFor(m => m.Configurations , new { CodeTypeItems = Model.CodeTypeItems })
...
Option 2 (generate a new SelectList in each iteration and set the selectedValue)
In this option your property CodeTypeItems should to be IEnumerable<GenericIdNameType>, not a SelectList (or just make codeTypes a public property). Then in the main view
#Html.DropDownListFor(m => m.Configurations[0].HelperCodeType, new SelectList(Model.CodeTypeItems, "Id", "Name", Model.Configurations[0].HelperCodeType)
Side note: there is no need to use new { id = "Configurations[0].HelperCodeType" - the DropDownListFor() method already generated that id attribute
I wrote this class to overcome an issue I was having with selecting an option in an html select list. I hope it helps someone.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
namespace Login_page.Models
{
public class HTMLSelect
{
public string id { get; set; }
public IEnumerable<string> #class { get; set; }
public string name { get; set; }
public Boolean required { get; set; }
public string size { get; set; }
public IEnumerable<SelectOption> SelectOptions { get; set; }
public HTMLSelect(IEnumerable<SelectOption> options)
{
}
public HTMLSelect(string id, string name)
{
this.id = id;
this.name = name;
}
public HTMLSelect(string id, string name, bool required, IEnumerable<SelectOption> options)
{
this.id = id;
this.name = name;
this.required = required;
}
private string BuildOpeningTag()
{
StringBuilder text = new StringBuilder();
text.Append("<select");
text.Append(this.id != null ? " id=" + '"' + this.id + '"' : "");
text.Append(this.name != null ? " name=" + '"' + this.name + '"' : "");
text.Append(">");
return text.ToString();
}
public string GenerateSelect(IEnumerable<SelectOption> options)
{
StringBuilder selectElement = new StringBuilder();
selectElement.Append(this.BuildOpeningTag());
foreach (SelectOption option in options)
{
StringBuilder text = new StringBuilder();
text.Append("\t");
text.Append("<option value=" + '"' + option.Value + '"');
text.Append(option.Selected != false ? " selected=" + '"' + "selected" + '"' + ">" : ">");
text.Append(option.Text);
text.Append("</option>");
selectElement.Append(text.ToString());
}
selectElement.Append("</select");
return selectElement.ToString();
}
}
public class SelectOption
{
public string Text { get; set; }
public Boolean Selected { get; set; }
public string Value { get; set; }
}
}
And
public IEnumerable<SelectOption> getOrderTypes()
{
List<SelectOption> orderTypes = new List<SelectOption>();
if (this.orderType == "OptionText")
{
orderTypes.Add(new SelectOption() { Value = "1", Text = "OptionText", Selected = true });
} else
{
orderTypes.Add(new SelectOption() { Value = "2", Text = "OptionText2" });
}
}
And to use it:
#{
Login_page.Models.HTMLSelect selectElement = new Login_page.Models.HTMLSelect("order-types", "order-types");
}
#Html.Raw(selectElement.GenerateSelect(Model.getOrderTypes()));
I leave this in case it helps someone else. I had a very similar problem and none of the answers helped.
We had in a view this line at the top:
IEnumerable<SelectListItem> exitFromTrustDeed = (ViewData["ExitFromTrustDeed"] as IEnumerable<string>).Select(e => new SelectListItem() {
Value = e,
Text = e,
Selected = Model.ExitFromTrustDeed == e
});
and then below in the view:
#Html.DropDownListFor(m => m.ExitFromTrustDeed, exitFromTrustDeed, new { #class = "form-control" })
We had a property in my ViewData with the same name as the selector for the lambda expression and for some reason that makes the dropdown to be rendered without any option selected.
We changed the name in ViewData to ViewData["ExitFromTrustDeed2"] and that made it work as expected.
Weird though.
I have table with the next columns
Id,Name,A_Status,BranchName.
Would be that expendiently to achive, with the next method, i described bellow?
Column branch has 25 different names. So, i want to create a page, with 25 titles of branch(and for each, create his own cshtml page), where user can enter and view data of that specific branch, he has chosen. On each of those pages specific permissions, not all users can enter and view them.
I decided to achive it with the next code.
My controller
public ActionResult A_Branch ()
{
string query = "SELECT BranchName,Name, MAX(A_STATUS) AS A_Status "
+ "FROM Students "
+ "WHERE BranchName= '1_Branch' "
+ "GROUP BY BranchName,Name";
IEnumerable<EnrollmentStudentGroup> data = db.Database.SqlQuery<EnrollmentStudentGroup>(query);
return View(data.ToList());
}
And the next actionresult which i'm going to create 23 times more.
public ActionResult B_Branch ()
{
string query = "SELECT BranchName,Name, MAX(A_STATUS) AS A_Status "
+ "FROM Students "
+ "WHERE BranchName= '2_Branch' "
+ "GROUP BY BranchName,Name";
IEnumerable<EnrollmentStudentGroup> data = db.Database.SqlQuery<EnrollmentStudentGroup>(query);
return View(data.ToList());
}
My 2 models;
public class Student : BaseEntity
{
public int Id { get; set; }
public string BranchName { get; set; }
public string Name { get; set; }
public int A_Status { get; set; }
}
public class EnrollmentStudentGroup
{
public string BranchName { get; set; }
public string Name { get; set; }
public int A_Status { get; set; }
public IEnumerable<EnrollmentStudentGroup> StudentCollection { get; set; }
}
and my Model
public class Model
{
public List<Student> Students { get; set; }
public List<EnrollmentStudentGroup> EnrollmentStudentGroup { get; set; }
}
My newest controller ( after " .select " he is giving me errors )
[HttpGet]
public ActionResult TestNew(string branchname)
{
// check stuff like permissions
var db = new MovieContext();
var model = new Model();
var students = db.Student
.Where(x => x.BranchName == branchname)
.GroupBy(x => new { x.BranchName, x.Name, x.Currency, x.NoCart, x.NoAccount })
.Select(x => new
{
BranchName = x.FirstOrDefault().BranchName,
Name = x.FirstOrDefault().Name,
A_Status = x.Max(p => p.A_Status),
Currency = x.FirstOrDefault().Currency,
NoCart = x.FirstOrDefault().NoCart,
NoAccount = x.FirstOrDefault().NoAccount
}).ToList();
foreach (var item in students)
{
model.Students.Add(new Student
{
A_Status = item.A_Status,
BranchName = item.BranchName,
Name = item.Name,
NoAccount = item.NoAccount,
NoCart = item.NoCart,
Currency = item.Currency
});
}
return View(model);
}
Now that error, i updated my model and view(cshtml) by your answer.
My view(cshtml)
#model Tessa.Models.Model
#{
ViewBag.Title = "BranchView";
}
<h2>BranchView</h2>
#foreach (var item in Model.Students)
{
<p>#item.Name</p>
<p>#item.A_Status</p>
<p>#item.BranchName</p>
<p>#item.NoAccount</p>
<p>#item.NoCart</p>
<p>#item.Currency</p>
}
As in the comments wrote you dont have to do this for every single branch. Here is a solutions which may help you
Your Controller
public ActionResult Branch(string branchname)
{
var db = new ApplicationDbContext();
var model = new Model();
var students = db.Students
.Where(x => x.BranchName == branchname)
.GroupBy(x => new { x.BranchName, x.Name, x.Currency, x.NoCart, x.NoAccount })
.Select(x => new
{
BranchName = x.FirstOrDefault().BranchName,
Name = x.FirstOrDefault().Name,
A_Status = x.Max(p => p.A_Status),
Currency = x.FirstOrDefault().Currency,
NoCart = x.FirstOrDefault().NoCart,
NoAccount = x.FirstOrDefault().NoAccount
}).ToList();
foreach (var item in students)
{
model.Students.Add(new Student
{
A_Status = item.A_Status,
BranchName = item.BranchName,
Name = item.Name,
NoAccount = item.NoAccount,
NoCart = item.NoCart,
Currency = item.Currency
});
}
return View(model);
}
So you only pass the branchname to the controller. Her are severale diffrent ways to do this.
Its good practice to have a model to pass to a view.
public class Model
{
public List<Student> Students { get; set; }
//Some other propertys
public Model()
{
Students = new List<Student>();
}
}
And finialy you can write your view like this
#model TestApp.Models.Model
#{
ViewBag.Title = "BranchView";
}
<h2>BranchView</h2>
#foreach (var item in Model.Students)
{
<p>#item.Name</p>
<p>#item.A_Status</p>
<p>#item.Branchname</p>
<p>#item.NoAccount</p>
<p>#item.NoCart</p>
<p>#item.Currency</p>
}
In the foreach-loop you can do normal html stuff. This will done for every element in your list.
I hope this help you.
my Model is
public class ChildMenu
{
public string Name { get; set; }
public string Comments { get; set; }
public List<UlrikenModel.ulriken_tblChildMenu> FormDetails { get; set; }
public long pkChildMenuID { get; set; }
public long fkSubMenuID { get; set; }
[Required(ErrorMessage = "Requird")]
public string ChildManuName { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
public string Events { get; set; }
public IList<SelectListItem> Drp_Submenu { get; set; }
}
My Controller action is :
public ActionResult FillDeptName()
{
UlrikenEntities db1 = new UlrikenModel.UlrikenEntities();
List<SelectListItem> list = new List<SelectListItem>();
list.Add(new SelectListItem { Text = "-Please select-", Value = "Selects
items" });
var cat = (from c in db1.ulriken_tblSubMenu where c.fkMainMenuID == 1 &&
c.Status == true select new { c.pkSubMenuID,c.SubManuName }).ToArray();
for (int i = 0; i < cat.Length; i++)
{
list.Add(new SelectListItem
{
Text = cat[i].SubManuName,
Value = cat[i].pkSubMenuID.ToString(),
Selected = (cat[i].pkSubMenuID == 1)
});
}
ViewBag.list = list;
return View("ChildMenuOfSubMenu", ViewBag.list);
}
[HttpPost]
[ValidateInput(false)]
public ActionResult ChildMenuOfSubMenu(ChildMenu obj)
{
UlrikenEntities db = new UlrikenEntities();
ulriken_tblChildMenu objchild = new ulriken_tblChildMenu();
objchild.fkSubMenuID = obj.fkSubMenuID;
objchild.ChildMenuName = obj.ChildManuName;
objchild.cPageBody = obj.Name;
db.ulriken_tblChildMenu.Add(objchild);
db.SaveChanges();
return View("ChildMenuOfSubMenu");
}
and view is
#Html.DropDownListFor(m=>m.fkSubMenuID,
(IEnumerable<SelectListItem>)ViewBag.list,"Select" ,new { id = "ddlSubMenu" })
At start dropdown bind successfully but after saving data to database show an exception in
as "There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key
'fkSubMenuID'"
AnyBody guide me where am i doing wrong.
Move that code to the helper class:
public class ControllerHelper
{
public List<SelectListItem> FetchListItems()
{
List<SelectListItem> list = new List<SelectListItem>();
list.Add(new SelectListItem { Text = "-Please select-", Value = "Selects items" });
var cat = (from c in db1.ulriken_tblSubMenu where c.fkMainMenuID == 1 &&
c.Status == true select new { c.pkSubMenuID,c.SubManuName }).ToArray();
for (int i = 0; i < cat.Length; i++)
{
list.Add(new SelectListItem
{
Text = cat[i].SubManuName,
Value = cat[i].pkSubMenuID.ToString(),
Selected = (cat[i].pkSubMenuID == 1)
});
}
return list;
}
}
And then your controller should looks like:
public ActionResult FillDeptName()
{
UlrikenEntities db1 = new UlrikenModel.UlrikenEntities();
ViewBag.list = new ControllerHelper().FetchListItems();
return View("ChildMenuOfSubMenu", ViewBag.list);
}
[HttpPost]
[ValidateInput(false)]
public ActionResult ChildMenuOfSubMenu(ChildMenu obj)
{
UlrikenEntities db = new UlrikenEntities();
ulriken_tblChildMenu objchild = new ulriken_tblChildMenu();
objchild.fkSubMenuID = obj.fkSubMenuID;
objchild.ChildMenuName = obj.ChildManuName;
objchild.cPageBody = obj.Name;
db.ulriken_tblChildMenu.Add(objchild);
db.SaveChanges();
ViewBag.list = new ControllerHelper().FetchListItems();
return View("ChildMenuOfSubMenu");
}
Of course:
new ControllerHelper().FetchListItems();
should be a field in the controller class, for example:
private ControllerHelper controlerHelper;
You can use Interface instead of concerete implementation, if you use DI.
Regards