There are some good answers here. I would add to them the following points.
What is the correct C# way of representing a data structure, which, "logically" (that is to say, "to the human mind") is just a list of things with a few bells and whistles?
Ask any ten non-computer-programmer people who are familiar with the existence of football to fill in the blank:
A football team is a particular kind of _____
Did anyone say "list of football players with a few bells and whistles", or did they all say "sports team" or "club" or "organization"? Your notion that a football team is a particular kind of list of players is in your human mind and your human mind alone.
List<T>
is a mechanism. Football team is a business object -- that is, an object that represents some concept that is in the business domain of the program. Don't mix those! A football team is a kind of team; it has a roster, a roster is a list of players. A roster is not a particular kind of list of players. A roster is a list of players. So make a property called Roster
that is a List<Player>
. And make it ReadOnlyList<Player>
while you're at it, unless you believe that everyone who knows about a football team gets to delete players from the roster.
Is inheriting from List<T>
always unacceptable?
Unacceptable to whom? Me? No.
When is it acceptable?
When you're building a mechanism that extends the List<T>
mechanism.
What must a programmer consider, when deciding whether to inherit from List<T>
or not?
Am I building a mechanism or a business object?
But that's a lot of code! What do I get for all that work?
You spent more time typing up your question that it would have taken you to write forwarding methods for the relevant members of List<T>
fifty times over. You're clearly not afraid of verbosity, and we are talking about a very small amount of code here; this is a few minutes work.
UPDATE
I gave it some more thought and there is another reason to not model a football team as a list of players. In fact it might be a bad idea to model a football team as having a list of players too. The problem with a team as/having a list of players is that what you've got is a snapshot of the team at a moment in time. I don't know what your business case is for this class, but if I had a class that represented a football team I would want to ask it questions like "how many Seahawks players missed games due to injury between 2003 and 2013?" or "What Denver player who previously played for another team had the largest year-over-year increase in yards ran?" or "Did the Piggers go all the way this year?"
That is, a football team seems to me to be well modeled as a collection of historical facts such as when a player was recruited, injured, retired, etc. Obviously the current player roster is an important fact that should probably be front-and-center, but there may be other interesting things you want to do with this object that require a more historical perspective.
The approach recommended by the ASP.Net Core team is to use the new policy design which is fully documented here. The basic idea behind the new approach is to use the new [Authorize]
attribute to designate a "policy" (e.g. [Authorize( Policy = "YouNeedToBe18ToDoThis")]
where the policy is registered in the application's Startup.cs
to execute some block of code (i.e. ensure the user has an age claim where the age is 18 or older).
The policy design is a great addition to the framework and the ASP.Net Security Core team should be commended for its introduction. That said, it isn't well-suited for all cases. The shortcoming of this approach is that it fails to provide a convenient solution for the most common need of simply asserting that a given controller or action requires a given claim type. In the case where an application may have hundreds of discrete permissions governing CRUD operations on individual REST resources ("CanCreateOrder", "CanReadOrder", "CanUpdateOrder", "CanDeleteOrder", etc.), the new approach either requires repetitive one-to-one mappings between a policy name and a claim name (e.g. options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder));
), or writing some code to perform these registrations at run time (e.g. read all claim types from a database and perform the aforementioned call in a loop). The problem with this approach for the majority of cases is that it's unnecessary overhead.
While the ASP.Net Core Security team recommends never creating your own solution, in some cases this may be the most prudent option with which to start.
The following is an implementation which uses the IAuthorizationFilter
to provide a simple way to express a claim requirement for a given controller or action:
public class ClaimRequirementAttribute : TypeFilterAttribute
{
public ClaimRequirementAttribute(string claimType, string claimValue) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] {new Claim(claimType, claimValue) };
}
}
public class ClaimRequirementFilter : IAuthorizationFilter
{
readonly Claim _claim;
public ClaimRequirementFilter(Claim claim)
{
_claim = claim;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value);
if (!hasClaim)
{
context.Result = new ForbidResult();
}
}
}
[Route("api/resource")]
public class MyController : Controller
{
[ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
[HttpGet]
public IActionResult GetResource()
{
return Ok();
}
}
Best Answer
Using the Select Tag helpers to render a SELECT element
In your GET action, create an object of your view model, load the
EmployeeList
collection property and send that to the view.And in your create view, create a new
SelectList
object from theEmployeeList
property and pass that as value for theasp-items
property.And your HttpPost action method to accept the submitted form data.
Or
If your view model has a
List<SelectListItem>
as the property for your dropdown items.And in your get action,
And in the view, you can directly use the
Employees
property for theasp-items
.The class
SelectListItem
belongs toMicrosoft.AspNet.Mvc.Rendering
namespace.Make sure you are using an explicit closing tag for the select element. If you use the self closing tag approach, the tag helper will render an empty SELECT element!
The below approach will not work
But this will work.
Getting data from your database table using entity framework
The above examples are using hard coded items for the options. So i thought i will add some sample code to get data using Entity framework as a lot of people use that.
Let's assume your DbContext object has a property called
Employees
, which is of typeDbSet<Employee>
where theEmployee
entity class has anId
andName
property like thisYou can use a LINQ query to get the employees and use the Select method in your LINQ expression to create a list of
SelectListItem
objects for each employee.Assuming
context
is your db context object. The view code is same as above.Using SelectList
Some people prefer to use
SelectList
class to hold the items needed to render the options.Now in your GET action, you can use the
SelectList
constructor to populate theEmployees
property of the view model. Make sure you are specifying thedataValueField
anddataTextField
parameters. You can use a nameof expression to link the field names statically.Here I am calling the
GetEmployees
method to get a list of Employee objects, each with anId
andFirstName
property and I use those properties asDataValueField
andDataTextField
of theSelectList
object we created. You can change the hardcoded list to a code which reads data from a database table.The view code will be same.
Render a SELECT element from a list of strings.
Sometimes you might want to render a select element from a list of strings. In that case, you can use the
SelectList
constructor which only takesIEnumerable<T>
The view code will be same.
Setting selected options
Some times,you might want to set one option as the default option in the SELECT element (For example, in an edit screen, you want to load the previously saved option value). To do that, you may simply set the
EmployeeId
property value to the value of the option you want to be selected.This will select the option Tom in the select element when the page is rendered.
Multi select dropdown
If you want to render a multi select dropdown, you can simply change your view model property which you use for
asp-for
attribute in your view to an array type.This will render the HTML markup for the select element with the
multiple
attribute which will allow the user to select multiple options.Setting selected options in multi select
Similar to single select, set the
EmployeeIds
property value to the an array of values you want.This will select the option Tom and Jerry in the multi select element when the page is rendered.
Using ViewBag to transfer the list of items
If you do not prefer to keep a collection type property to pass the list of options to the view, you can use the dynamic ViewBag to do so.(This is not my personally recommended approach as viewbag is dynamic and your code is prone to uncaught typo errors)
and in the view
Using ViewBag to transfer the list of items and setting selected option
It is same as above. All you have to do is, set the property (for which you are binding the dropdown for) value to the value of the option you want to be selected.
and in the view
Grouping items
The select tag helper method supports grouping options in a dropdown. All you have to do is, specify the
Group
property value of eachSelectListItem
in your action method.There is no change in the view code. the select tag helper will now render the options inside 2 optgroup items.