Excessive Null checking in MVC Views - model-view-controller

This is a question about style and design rather than syntax.
I have domain models that have several (for lack of a better term) navigation properties. So in my strongly typed details view of Foo which has a property of type Bar I could do the following:
<%: Foo.Bar.Name %>
However, sometimes Bar is Null, so I end up with something like:
<%: Foo.Bar == null ? String.Empty : Foo.Bar.Name %>
In other cases, because the convenience of the navigation properties I could do even more chaining. The downside, however, is the introduction of more null checking in my View.
As an alternative, I could do all the null checking in a ViewModel so that I've passed off something "clean" to the View. I'm looking for some ideas or common sense guidelines for avoiding excessive null checking in my views.
P.S. I'm using ASP.NET MVC, but I think this question may be relevant to other MVC frameworks.

You already answered it yourself:
As an alternative, I could do all the null checking in a ViewModel so that I've passed off something "clean" to the View.
That's it, use a ViewModel.

Not sure whether it really makes sense in your context, but when you are bothered by an excess of null-checks, you should contemplate the Null Object pattern.

Create a helper that checks for null and returns an appropriate default.
For example if you have a phone number field, you pass it the field and a constant PHONE. the helper checks if null and displays "No phone number Available" if it is null.
This way you keep all your null checking in one place.

One option is using a helper, as described here.

You could provide lazy initialization for your navigation properties in you model. That way you could minimize the risk of encountering null values.
public class Foo
{
private Bar _bar;
public Bar Bar
{
get
{
if (_bar == null)
{
_bar = new Bar();
}
return _bar;
}
}
}
public class Bar
{
public string Name { get; set; }
}

First consider whether in your model an instance of Foo is valid if Bar is null. If no then you should put a guard clause in the constructor and\or the setter of the Bar property.
public class Foo{
public BarClass _bar;
public BarClass Bar{
get{return _bar; }
set{
if(value == null)
throw new ArgumentNullException();
_bar = value; }
}
public Foo(BarClass bar){
if(bar == null) throw new ArgumentNullException();
_bar = bar;
}
}
Secondly, too many and duplicated null checks is a code smell. You should use the refactoring Replace Conditional with Polymorphism. You can create a Null object according to the Null Object Pattern. Then make sure that the Bar property is always initialized with NullBar.
public class Foo{
public BarClass _bar;
public BarClass Bar{
get{return _bar ?? new NullBar(); }
set{ _bar = value ?? new NullBar(); }
}
public Foo(){
Bar = new NullBar(); // initialization
}
}
Then Bar will never be null.

Related

ReactiveUI : Binding lost after exception in property setter

In a WinForms ReactiveUI ViewModel I have a property with a property setter that can throw an ArgumentException:
public string Foo
{
get { return _foo; }
set
{
if (value == "ERR" ) throw new ArgumentException("simulate an error");
this.RaiseAndSetIfChanged(ref _foo, value);
Debug.WriteLine(string.Format("Set Foo to {0}", _foo));
}
}
private string _foo;
In View the property Foo is bind to a textbox uiFoo:
this.Bind(ViewModel, vm => vm.Foo, v => v.uiFoo.Text);
Binding works properly (as shown by the output of the setter’s Debug.WriteLine).
But after typing “ERR” which throws ArgumentException the binding no longer works.
What solution do I have to bring back (or keep) binding in working state after the exceptions in setter ?
As we've mentioned in the comments on your question, exceptions inside property getter or setters is a BIG NO, NO.
Here are two solutions available to you: Either handle the invalid value when you try to read it or use a setter method. I would much prefer the latter; especially as you're throwing an ArgumentException, which should only be used when arguments are supplied. (Breaking this rule might be confusing for the consumer of the code: "Why am I getting an ArgumentException when I'm not calling any functions?")
Here's a way to do it:
public string Foo { get; private set; }
public void SetFoo(string value)
{
if(value == "invalid value")
thrown new ArgumentException($"{nameof(Foo)} can't be set to '{value}'", nameof(value));
Foo = value;
}

Validation for items in ObservableCollection bound to DataGrid when validation of one item of collection depends on other items

I am using MVVM and displaying some items on a DataGrid. My model is RecordingInfo and looks like:
public class RecordingInfo : IDataErrorInfo
{
public RecordingInfo(string fullDirectoryName, string recordingName, int recordingNumber)
{
FullDirectoryName = fullDirectoryName;
RecordingName = recordingName;
RecordingNumber = recordingNumber;
}
public string FullDirectoryName { get; internal set; }
public string RecordingName { get; set; }
public int RecordingNumber { get; internal set; }
public string Error
{
get { throw new NotImplementedException(); }
}
public string this[string propertyName]
{
get {
if (propertyName == "RecordingName")
{
if (this.RecordingName.Length < 2)
return "Recording Name must be at least two characters";
}
return null;
}
}
}
I end up with a collection of these RecordingInfo programmatically. The user is not allowed to do much with these but he/she can change the RecordingName subject to the name being 2 characters or more AND that the RecordingName must be unique. I.e. no changing it to match another RecordingName. I have taken care of the first requirement. It's the second one that is giving me grief.
For my ViewModel, I have
public class RecordingListViewModel : ViewModelBase//, IDataErrorInfo
{
private ObservableCollection<RecordingInfo> _recordings = null;
public RecordingListViewModel()
{
}
public ObservableCollection<RecordingInfo> Recordings
{
get
{
return _recordings;
}
}
// more stuff left off for brevity
In my view I bind the collection to a DataGrid and have:
<DataGrid ItemsSource="{Binding Path=Recordings}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Recording" IsReadOnly="False" EditingElementStyle="{StaticResource CellEditStyle}" ElementStyle="{StaticResource CellNonEditStyle}" Binding="{Binding RecordingName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" >
</DataGridTextColumn>
...
</DataGrid.Columns>
My way of checking for 2 or more characters works great. But this doesn't work for checking that the user is not trying to give a recording an existing name. Presumably, I need to somehow handle this at the ViewModel layer since the ViewModel knows about all Recordings. I tried playing with having my ViewModel derive from IDataErrorInfo but the property indexer never gets called, which makes sense as it's the Observable collection and therefore the individual RecordingInfos that are bound. I also thought about doing something with a "Lost Focus" event, but DataGridTextColumn doesn't seem to have that. I would think this is a somewhat common problem: validation must take into account relationships between the items of the collection.
By the way, I'm not wedded to the IDataErrorInfo and I am not opposed to other changes in architecture. Please let me know if I can provide more details. I have tried to provide a minimal amount of code. Clearly, this is part of a much bigger project. Any advice is appreciated.
Thanks,
Dave
I would do the following
1) Make RecordingInfo implement INotifyPropertyChanged
2) Use a BindingList<> instead of ObservableCollection<>
In your viewmodel, subscribe to the BindingList.ListChanged Event. This event will fire when items are added and removed, but also when the top level properties on RecordingInfo changes. In the case of a property being changed, the ListChangedEventArgs.PropertyDescriptor property will contain the name of the property, if you want to run validation for just that property (be careful though, this can be null when the item as added/removed). You'll need to use the ListChangedType property to determine the reason of the event (E.x.: Reset means everything changed, ItemAdded means the item was added, but the ItemChanged means a property changed as occurred on an existing item.
You can have the parent ViewModel (that contains and creates your RecordingInfos) pass a name validation Func in their constructors for them to call when validating their name changes.

EF, POCO, DbContext and Validating Deletions

I'm fairly new to the world of MVC and EF, and I've come quite a ways on my own, but one thing I haven't been able to find online is how people validate for "do not delete" conditions.
I'm using EF4.1 database-first POCO classes generated with the DbContext T4 template. In my partial class files I've already decorated all of my classes with the "IValidatableObject" interface that gets called on changes for my business rules that go beyond the standard MetaData attribute type of validations.
What I need now is a validation that works via the same mechanism (and is, therefore, transparent to the UI and the controller) for checking if deletions are OK. My thought was to create an interface like so:
public interface IDeletionValidation
{
DbEntityValidationResult ValidateDeletion(DbEntityValidationResult validationResults);
}
...and then do this in an override to ValidateEntity in the DbContext...
public partial class MyEntityContext
{
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
DbEntityValidationResult val = base.ValidateEntity(entityEntry, items);
if (entityEntry.State == EntityState.Deleted)
{
IDeletionValidation delValidationEntity = entityEntry.Entity as IDeletionValidation;
if (delValidationEntity != null)
val = delValidationEntity.ValidateDeletion(val);
}
return val;
}
...and then I could implement the IDeletionValidation interface on those classes that need to have a validation done before they can be safely deleted.
An example (not working, see caveat in comments) of the ValidateDeletion code would be...
public partial class SalesOrder : IDeletionValidation, IValidatableObject
{
public DbEntityValidationResult ValidateDeletion(DbEntityValidationResult validations)
{
// A paid SalesOrder cannot be deleted, only voided
// NOTE: this code won't work, it's coming from my head and note from the actual source, I forget
// what class I'd need to add to the DbEntityValidationResult collection for this type of validation!
if (PaidAmount != 0)
validations.Add(new ValidationResult("A paid SalesOrder cannot be deleted, only voided"));
return validations;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> validations = new List<ValidationResult>();
// Verify that the exempt reason is filled in if the sales tax flag is blank
if (!IsTaxable && string.IsNullOrEmpty(TaxExemptReason))
validations.Add(new ValidationResult("The Tax Exempt Reason cannot be blank for non-taxable orders"));
return validations;
}
....
}
Am I on the right track? Is there a better way?
Thanks,
CList
EDIT --- Summary of the one-interface method proposed by Pawel (below)
I think the one-interface way presented below and my way above is a little bit of a chocolate vs. vanilla argument in terms of how you want to do it. Performance should be about the same for large numbers of updates / deletes, and you may want to have your delete validation be a separate interface that doesn't apply to all of your validated classes, but if you want all of your validations in one place here it is...
Mod your DBContext
protected override bool ShouldValidateEntity(DbEntityEntry entityEntry)
{
return entityEntry.Sate == EntityState.Deleted ||
base.ShouldValidateEntity(entityEntry);
}
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var myItems = new Dictionary<object, object>();
myItems.Add("IsDelete", (entityEntry.State == EntityState.Deleted));
// You could also pass the whole context to the validation routines if you need to, which might be helpful if the
// validations need to do additional lookups on other DbSets
// myItems.Add("Context", this);
return base.ValidateEntity(entityEntry, myItems);
}
Put the deletion-validation in your entity's Validate
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> validations = new List<ValidationResult>();
bool isDelete = validationContext.Items.ContainsKey("IsDelete")
? (bool)validationContext.Items["IsDelete"]
: false;
if (isDelete)
{
if (PaidAmount != 0)
validations.Add(new ValidationResult("You cannot delete a paid Sales Order Line", new string[] { "PaidAmount" }));
return validations;
}
// Update / Add validations!!
// Verify that the exempt reason is filled in if the sales tax flag is blank
if (!IsTaxable && string.IsNullOrEmpty(TaxExemptReason))
validations.Add(new ValidationResult("The Tax Exempt Reason cannot be blank for non-taxable orders"));
return validations;
}
...and in the interest of brevity and only putting all of the check-if-delete code in one place, you could even create an extension method on the ValidationContext class (if you're into that sort of thing) like so...
public static class MyExtensions
{
public static bool IsDelete(this System.ComponentModel.DataAnnotations.ValidationContext validationContext)
{
return validationContext.Items.ContainsKey("IsDelete")
? (bool)validationContext.Items["IsDelete"]
: false;
}
}
...which gives us this for our validation code...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> validations = new List<ValidationResult>();
if (validationContext.IsDelete())
{
....
I am not really sure why you need a separate interface just for deleted entities. You could pass the entity state (or the EntityEntry object, or the context) to your IValidatableObject.Validate() method by using the items dictionary you pass to the base.ValidateEntity() method. Take a look at the "Custom Validation Sample: Uniqness" section in this blog post http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx. This way you could do everything using just one interface - IValidatableObject.
In addition to that - by default EF validates only Added and Modified entities. If you want to validate entities that are in the Deleted state you need to override DbContext.ShouldValidateEntity() method with something like this:
protected override bool ShouldValidateEntity(DbEntityEntry entityEntry)
{
return entityEntry.Sate == EntityState.Deleted ||
base.ShouldValidateEntity(entityEntry);
}

Get full name of Complex Type from ModelClientValidationRequiredIfRule method in custom ValidationAttribute

I am using the example at The Complete Guide To Validation In ASP.NET MVC 3 to create a RequiredIf validation attribute (it's about 1/3 down the page under the heading of "A more complex custom validator"). It all works fine with the exception of one scenario, and that is if I have the need to validate against a complex type. For example, I have the following model:
public class MemberDetailModel
{
public int MemberId { get; set; }
// Other model properties here
public MemberAddressModel HomeAddress { get; set; }
public MemberAddressModel WorkAddress { get; set; }
}
public class MemberAddressModel
{
public bool DontUse { get; set; }
// Other model properties here
[RequiredIf("DontUse", Comparison.IsEqualTo, false)]
public string StreetAddress1 { get; set; }
}
The problem is that when the attribute validation for the StreetAddress property is rendered, it get's decorated with the attribute of data-val-requiredif-other="DontUse". Unfortunately, since the address is a sub-type of the main model, it needs to be decorated with a name of HomeAddress_DontUse and not just DontUse.
Strangely enough, the validation works fine for server-side validation, but client-side unobtrusive validation fails with an JS error because JS can't find the object with a name of just "DontUse".
Therefore, I need to find a way to change the ModelClientValidationRequiredIfRule method to know that the property it is validating is a sub-type of a parent type, and if so, prepend the ParentType_ to the "otherProperty" field (e.g. otherProperty becomes HomeAddress_DontUse.
I have tried passing in typeof(MemberAddressModel) as a parameter of the attribute, but even when debugging the attribute creation, I can't seem to find any reference to the parent type of HomeAddress or WorkAddress from that type.
Based on the suggestion from The Flower Guy, I was able to come up with the following which seems to work. I simply modified the following in the customValidation.js file:
jQuery.validator.addMethod("requiredif", function (value, element, params) {
if ($(element).val() != '') return true;
var prefix = getModelPrefix(element.name); // NEW LINE
var $other = $('#' + prefix + params.other); // MODIFIED LINE
var otherVal = ($other.attr('type').toUpperCase() == "CHECKBOX") ? ($other.attr("checked") ? "true" : "false") : $other.val();
return params.comp == 'isequalto' ? (otherVal != params.value) : (otherVal == params.value);
});
I also added the following method to that file (within the JQuery block so as to be only privately accessible):
function getModelPrefix(fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf(".") + 1).replace(".","_");
}
Cannot do it exactly right now, but the problem is in the client javascript function:
jQuery.validator.addMethod("requiredif" ...
The js is not sophisticated enough to cope with complex view models where there may be a model prefix. If you take a look at Microsoft's jquery.validate.unobstrusive.js (in the Scripts folder over every MVC3 application), you will find some useful methods including getModelPrefix and appendModelPrefix. You can take a similar approach and change the requiredIf validation method - take a look at the equalto method in jquery.validate.unobstrusive.js for a helping hand.

mvvmlight - what's the "proper way" of picking up url parameters for a view model

I'm just switching a project across to mvvmlight and trying to do things "the right way"
I've got a simple app with a listbox
When an item is selected in the listbox, then I've hooked up a RelayCommand
This RelayCommand causes a call on an INavigationService (http://geekswithblogs.net/lbugnion/archive/2011/01/06/navigation-in-a-wp7-application-with-mvvm-light.aspx) which navigates to a url like "/DetailPage.xaml?DetailId=12"
The DetailPage.xaml is then loaded and ... this is where I'm a bit unsure...
how should the DetailPage get hooked up to a DetailView with DetailId of 12?
should I do this in Xaml somehow using a property on the ViewLocator?
should I do this in the NavigatedTo method?
Please feel free to point me to a full sample - sure this has been done a (hundred) thousand times before, but all the blogs and tutorials seem to be skipping this last trivial detail (focussing instead on the messaging and on the ioc on on the navigationservice)
Thanks!
The only place you can retrieve the URL parameter is in the view. So since your view is likely depending on it, you should fetch it in the OnNavigatedTo method.
Then, you should pass it along to your viewmodel, either using messaging (to expensive if you ask me), or by referring to your datacontext (which is the viewmodel I presume), and execeuting a method on that.
private AddTilePageViewModel ViewModel
{
get
{
return DataContext as AddTilePageViewModel;
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var postalCode = NavigationContext.TryGetKey("PostalCode");
var country = NavigationContext.TryGetStringKey("Country");
if (postalCode.HasValue && string.IsNullOrEmpty(country) == false)
{
ViewModel.LoadCity(postalCode.Value, country);
}
base.OnNavigatedTo(e);
}
I'm using some special extensions for the NavigationContext to make it easier.
namespace System.Windows.Navigation
{
public static class NavigationExtensions
{
public static int? TryGetKey(this NavigationContext source, string key)
{
if (source.QueryString.ContainsKey(key))
{
string value = source.QueryString[key];
int result = 0;
if (int.TryParse(value, out result))
{
return result;
}
}
return null;
}
public static string TryGetStringKey(this NavigationContext source, string key)
{
if (source.QueryString.ContainsKey(key))
{
return source.QueryString[key];
}
return null;
}
}
}
Create a new WindowsPhoneDataBound application, it has an example of how to handle navigation between views. Basically you handle the navigation part in your view, then set the view's DataContext accord to the query string. I think it plays nicely with the MVVM pattern since your ViewModels don't have to know anything about navigation (which IMO should be handled at the UI level).

Resources