I'm going nuts every time I see the code similar to the following snippet.
/* Given:
* IDictionary<TKey, TValue> dictionary;
* TKey key;
*/
if (dictionary.ContainsKey(key)
{
var value = dictionary[key];
// do something with retrieved value
}
You should really use TryGetValue instead:
/* Given:
* IDictionary<TKey, TValue> dictionary;
* TKey key;
*/
TValue value;
if (dictionary.TryGetValue(key, out value)
{
// do something with retrieved value
}
And here is a couple of reasons why:
- It is more performant as the location of the value calculated only once. And in fact implementation of
ContainsKey
, Item
and TryGetValue
is exactly the same.
- In the first example the dictionary could be changed between
ContainsKey
and Item
method calls, and the retrieving of the item could fail in multi-threaded environment.
I was so crazy about this, so couple of month ago I wrote a plugin for ReSharper which can analyze and optimize such subsequent calls.
This is an translation of my article from 2nd April of 2012.
From the first version of ASP.NET MVC couple of years ago I found that there is no common solution how to display drop down lists properly. Probably every of you asked yourself: “How to pass the selection list to a drop down list?” And so I was asking the same question to myself, I could not even sleep;)
Introduction
For example we have an form to create a film, and we need to select film's genre from a drop down list. I'm not going to show any "newbuy" solutions as, for instance, getting the genres in the view file.
"Head on" solution
The programmers bumping that problem try to solve it by brute force: they create additional property "Genres" of type SelectList at the model class, and they fill it at the controller's action. Like this:
The model
public class Movie {
public int GenreId { get; set; }
public SelectList Genres { get; set; }
//...
}
The controller
public class MoviesController {
[HttpGet] public ActionResult Create() {
var model = new Movie() { Genres = GetAllGenresFromDatabase(); }
return View(model);
}
[HttpPost] public ActionResult Create(Movie form) {
// do something with movie.
}
[HttpGet] public ActionResult Edit(int id) {
var model = GetMovieFromDatabase();
model.Genres = GetAllGenresFromDatabase();
return View(model);
}
[HttpPost] public ActionResult Edit(EditMovie form) {
// do something with movie.
}
//...
}
This works so far, but the solution has some major problems
- Model has redundant fields
- Need to create a model for a creation form (when there is no entity in DB yet)
- Code duplication for select list options retrieval logic. This problem grows if you need to select form the same list in many places.
- Not possible to use
Html.EditorForModel()
to display all you form at once with auto-generated markup, as we do not have access to neighbour fields when ASP.NET renders the model
Solution using ViewBag / ViewData
The solution is similar to the previous one with one simple change: selection list is passing through ViewBag / ViewData
The model
public class Movie {
[UIHint("Genres")]
public int GenreId { get; set; }
//...
}
The controller
public class MoviesController {
[HttpGet] public ActionResult Create() {
ViewBag.Genres = GetAllGenresFromDatabase();
return View();
}
[HttpPost] public ActionResult Create(Movie form) {
// do something with movie.
}
[HttpGet] public ActionResult Edit(int id) {
var model = GetMovieFromDatabase();
ViewBag.Genres = GetAllGenresFromDatabase();
return View(model);
}
[HttpPost] public ActionResult Edit(EditMovie form) {
// do something with movie.
}
//...
}
Pros over the previous solution
- The model does not have redundant fields
- Not needed to create a model for a creation form (when there is no entity in DB yet)
- Possible to use
Html.EditorForModel()
with own Editor Template for every drop-down list field
Cons
- Using of the dynamics or magic-strings*, which complicates refactoring and code modification
- Code duplication for select list options retrieval logic
- Custom editor template per field
Improved solution with ViewBag / ViewData
To improve the solution and reduce code duplication of retrieving of selection options logic we move the code from the controller action to an action filter
The model
The same as in the previous example
ActionFilter
public class PopulateGenresAttribute: ActionFilterAttribute {
public override void OnActionExecuted(ActionExecutedContext filterContext) {
filterContext.Controller.ViewData["Genres"] = GetAllGenresFromDatabase();
}
//...
}
The controller
public class MoviesController {
[HttpGet, PopulateGenres] public ActionResult Create() {
return View();
}
[HttpPost] public ActionResult Create(Movie form) {
// do something with movie.
}
[HttpGet, PopulateGenres] public ActionResult Edit(int id) {
var model = GetMovieFromDatabase();
return View(model);
}
[HttpPost] public ActionResult Edit(EditMovie form) {
// do something with movie.
}
//...
}
Pros over the previous solution
- No code duplication for select list options retrieval logic
Cons
- Using of the dynamics or magic-strings*, which complicates refactoring and code modification
- Custom editor template per field
- Application specific logic in the filter, which smells
Imporved solution with ViewBag / ViewData + MvcExtnsions
MvcExtensions have beautiful methdos to work with drop down lists: AsDropDownList / AsListBox (the first one for drop down list and the second one for multi-select lists). These are extension methods for model metadata builder. These methods set the template and allow to pass the ViewData's field name which stores the selection list to the View. So we are solving the need of having an template per field.
The model
public class Movie {
public int GenreId { get; set; }
}
The metadata
public class MovieMetadata : ModelMetadataConfiguration {
public MovieMetadata {
Configure(movie => movie.GenreId).AsDropDownList("Genres"/*шаблон*/);
}
}
The controller
The same as in the previous example
Pros over the previous solution
- Using of two generic templates (DropDownList / ListBox) for all lists, or specific one if needed
Cons
- Using of the dynamics or magic-strings*, which complicates refactoring and code modification
The solution with ChildAction
This is a good idea to put logic of the selection list to a separate action. This will lead to better separation of concern and give you some benifits like caching. But yf you'll try to use just a child action, then it'll not work at all: you will not have client-side validation from the box, you will not be able to use nested forms, etc., because the field names will be broken. To make it work properly you need to set view data's model metadata to the same as in parent action.
The model
public class Movie {
[UIHint("Genres")]
public int GenreId { get; set; }
}
The controllers
public class MoviesController {
[HttpGet] public ActionResult Create() {
return View();
}
[HttpPost] public ActionResult Create(Movie form) {
// do something with movie.
}
[HttpGet] public ActionResult Edit(int id) {
var model = GetMovieFromDatabase();
return View(model);
}
[HttpPost] public ActionResult Edit(EditMovie form) {
// do something with movie.
}
}
public class GenresController {
public ActionResult List() {
var selectedGenreId = this.ControllerContext.ParentActionViewContext.ViewData.Model as int?;
var genres = GetGenresFormDatabase();
var model = new SelectList(genres, "Id", "DisplayName", selectedGenreId);
this.ViewData.Model = model;
this.ViewData.ModelMetadata = this.ControllerContext.ParentActionViewContext.ViewData.ModelMetadata;
return View("DropDown");
}
}
Pros over the previous solution
- No using of dynamics and magic-strings
- No code duplication of select list options retrieval logic
- No application specific logic in ActionFilter
Cons
- Duplication of the boilerplate code
- Custom template per select list
- The Post-Redirect-Get scenario is not supported
Solution using ChildAction + MvcExtensions
I decided to imporve the previouse solution and apply the ActionFilter experience, and now, from the version 2.5.0-rc8000 MvcExtension do support child-action based drop down lists out of the box. I've added extension methods, which allows to use RenderAction to render the model fields. Also SelectListActionAttribute
attibute were added, which serves the action providing selection list data. Also this solution supports Post-Redirect-Get scenario.
The model
public class Movie {
public int GenreId { get; set; }
}
The metadata
public class MovieMetadata : ModelMetadataConfiguration {
public MovieMetadata {
Configure(movie => movie.GenreId).RenderAction("List", "Genres");
}
}
The controllers
public class MoviesController {
[HttpGet] public ActionResult Create() {
return View();
}
[HttpPost] public ActionResult Create(Movie form) {
// do something with movie.
}
[HttpGet] public ActionResult Edit(int id) {
var model = GetMovieFromDatabase();
return View(model);
}
[HttpPost] public ActionResult Edit(EditMovie form) {
// do something with movie.
}
}
public class GenresController {
[ChildActionOnly, SelectListAction] public ActionResult List(int selected) {
var model = GetGenresFormDatabase(selected);
return View("DropDown", model);
}
}
Pros over the previous solution
- No boilerplate code duplication
- Use an generic template
- MultiSelect "out of the box"
- Post-Redirect-Get is supported
Ending
For me, as a developer of MvcExtensions the methdos using the library is preferrable.
The sample of code with ViewBag / ViewData + MvcExtensions is here: http://github.com/MvcExtensions/Core/tree/master/samples
The sample of code with ChildAction + MvcExtensions is here: http://github.com/hazzik/DropDowns
*magic-strings can be easily solved by using of constans, and so because of that, I prefere using string over dynamic properties.
PS: The MvcExtensions abilities to extend ASP.NET MVC are limitless
The purpose of this attribute is to be able to render action as an editor or display template for your form fields. First of all we will need the data structure which will hold options for the our editor template.
RenderActionOptions.cs
public class RenderActionOptions
{
public static string MetadataKey = "RenderActionOptions";
public string Action { get; set; }
public string Controller { get; set; }
public string Area { get; set; }
}
Then we can use custom attribute, which implements IMetadataAware interface, to fill the options and pass them to a metadata.
RenderActionAttribute.cs
public class RenderActionAttribute : Attribute, IMetadataAware
{
public RenderActionAttribute([AspMvcAction] string action)
{
Action = action;
}
public RenderActionAttribute([AspMvcAction] string action, [AspMvcController] string controller)
{
Action = action;
Controller = controller;
}
public RenderActionAttribute([AspMvcAction] string action, [AspMvcController] string controller, [AspMvcController] string area)
{
Action = action;
Controller = controller;
Area = area;
}
public string Action { get; set; }
public string Controller { get; set; }
public string Area { get; set; }
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues[RenderActionOptions.MetadataKey] = new RenderActionOptions
{
Action = Action,
Controller = Controller,
Area = Area
};
metadata.TemplateHint = "RenderAction";
}
}
And finally at our EditorTemplate we will retrieve the options from the metadata and use Html.RenderAction
helper method to render our action. Please note, that Html.RenderAction
nor Html.Action
do not accept "area" as an argument, so we need to add the "area" key to our routes dictionary. Also, we pass data
to the Html.RenderAction
method to be able supply some additional information/metadata to the rendered action.
Shared/EditorTemplates/RenderAction.cshtml
@{
var data = new RouteValueDictionary(ViewData);
object o;
ViewData.ModelMetadata.AdditionalValues.TryGetValue(RenderActionOptions.MetadataKey, out o);
var options = o as RenderActionOptions;
if (!string.IsNullOrEmpty(options.Area))
{
data["area"] = options.Area;
}
if (!string.IsNullOrEmpty(options.Controller))
{
Html.RenderAction(options.Action, options.Controller, data);
}
else
{
Html.RenderAction(options.Action, data);
}
}
Example of Usage
MyModel.cs
public class MyModel
{
[Display(Name = "City"), RenderAction("ListCities", "Cities")]
public int CityId { get; set; }
}
MyForm.cshtml
@model MyModel
// ...
@Html.EditorFor(m => m.CityId, new { htmlAttributes = new { @class = "my-editor" } } )
// ...
In this blog post I want to present a little library which I wrote a while ago. This library can decompile methods to their λ-representation.
Why
Sometimes you need to use computable properties in LINQ queries, for example you have Employee class with computable property FullName
class Employee
{
public string FullName
{
get { return FirstName + " " + LastName; }
}
public string LastName { get; set; }
public string FirstName { get; set; }
}
And now your manager comes to you and says that we need to have ability to search by employee’s full name. And you without any thinking write following query:
var employees = (from employee in db.Employees
where (employee.FirstName + " " + employee.LastName) == "Test User"
select employee).ToList();
Yes, with such simple property as FullName it can work, but what will you do if field would be not simple as this one? For example there is one field from one of my previous projects, and it should be queryable!
class WayPoint
{
// all other is skipped due to clarity reasons
public virtual bool IsValid
{
get
{
return (Account == null) ||
(Role == null || Account.Role == Role) &&
(StructuralUnit == null || Account.State.StructuralUnit == StructuralUnit);
}
}
}
This is much harder…
What do we have to solve such puzzles?
<formula> element in NHibernate
If you are using NHibernate then you can map this field as formula, but this way is full of big scare bears, and such code is hard to maintain, and the <formula>
element supports only small subset of SQL, and if you are developing software which have to work with different RDBMS you have to be very-very careful.
It works only in NHibernate
With this great and powerful library we would rewrite our class like this
class Employee
{
private static readonly CompiledExpression fullNameExpression
= DefaultTranslationOf.Property(e => e.FullName).Is(e => e.FirstName + " " + e.LastName);
public string FullName
{
get { return fullNameExpression.Evaluate(this); }
}
public string LastName { get; set; }
public string FirstName { get; set; }
}
var employees = (from employee in db.Employees
where employee.FullName == "Test User"
select employee).WithTranslations().ToList()
All right, and the query looks good but the field declaration is terrible! And Evaluate compiles the λ-expression in runtime, which is no less terrible than the computable field decalaration.
And now meet my small library - DelegateDecompiler
DelegateDecompiler
The property should be marked with [Computed] attribute and it is all you need, and add .Decompile()
method invocation to the query.
class Employee
{
[Computed]
public string FullName
{
get { return FirstName + " " + LastName; }
}
public string LastName { get; set; }
public string FirstName { get; set; }
}
var employees = (from employee in db.Employees
where employee.FullName == "Test User"
select employee).Decompile().ToList()
In my opinion it is gracefully (If you don’t blow your own horn, no one will do it for you). But bear in mind that the source of your computable property should be supported using LINQ provider.
How does it work?
.Decompile()
method finds all the properties and methods marked with [Computed]
attribute and expand them to their source expressions. In other words the query above will become as following query:
var employees = (from employee in db.Employees
where (employee.FirstName + " " + employee.LastName) == "Test User"
select employee).ToList();
The library uses Mono.Reflection (GitHub, NuGet) as decompiler. The Mono.Reflection library is written by Jean-Baptiste Evain the creator of Mono.Cecil.
PS: This is an alpha version, use at your own risk.
Links