Today I created a new Mercurial repository on a Windows server. I cloned it, made some changes, tried to push, and was greeted with this:
C:\myapplication>hg push
pushing to http://servername/myapplication
searching for changes
abort: HTTP Error 500: .hg\store0changelog.i: Access is denied
My user account had write permission to the myapplication folder on the server, and the odd thing is that I’ve created repositories there before and never had a problem pushing changes. I compared 00changelog.i to the same file in another repository that was working. Turns out I was using anonymous authentication and IUSR was missing write permission. I gave full control to IUSR on hg\store folder and…
C:\myapplication>hg push
pushing to http://servername/myapplication
searching for changes
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 114 changes to 114 files
Success!
If you’re having problems pushing to a central server with Mercurial, make sure the IIS anonymous authentication account (IUSR or IUSR_MachineName) you have write permission to the hg\store folder and subfolders in your repository.
In the simple blog engine I’m building, I encountered a scenario where I wanted to display different UI elements depending on whether the user was logged in:
Not logged in
Logged in
When the user is authenticated, their name and email is known, so it’s unnecessary to display them on the comment input form. While it would be possible to hide just those fields, I decided to make separate partial views and display them in the Post view with the following code:
<%
if (Model.AllowComments)
{
var commentControlName = Request.IsAuthenticated ? AuthenticatedCommentFormControl" : "CommentFormControl";
Html.RenderPartial(commentControlName, new DotBlog.Models.ViewModels.CommentViewModel(Model.PostId));
}
%>
This is the view model I used for both partial views:
public class CommentViewModel
{
...
[Required]
[StringLength(50)]
[DisplayName("Name")]
public string CommenterName { get; set; }
public DateTime Date { get; set; }
[Required]
[StringLength(100)]
public string Email { get; set; }
[StringLength(100)]
[DisplayName("Web Site")]
public string WebSite { get; set; }
[Required]
[StringLength(1024)]
[DisplayName("Comment")]
public string CommentText { get; set; }
...
}
As you can see, I’m using data annotations to enforce required fields and string lengths. The problem is, even though the name and email aren’t always displayed, they are still required. I needed a way to validate certain fields conditionally. Here are the options I came up with:
Stop using data annotations and find a different solution for validation
Use two different view models: one for each partial view
When the form is posted, remove the fields from the model state so they’re not validated.
Let’s look at each of these individually:
Stop Using Data Annotations
Data annotations are not the only way to specify validation rules, but they’re easy to implement. xVal was fine for ASP.MVC 1.0, but is now deprecated. Custom validation code with ModelState.AddModelError() is flexible, but requires extra work.
Use Two Different View Models
Since I’m using two partial views for the comment form (one for authenticated users and one for guests), I could use a dedicated view model for each. One would omit the data annotations on name and email and therefore not cause any validation problems. This was my original implementation, but lead to unnecessary code duplication and other design problems.
Use Conditional Data Annotations
Simon Ince has built a cool library for conditional annotations that allows you to write code like this:
public class ValidationSample
{
[RequiredIf("PropertyValidationDependsOn", true)]
public string PropertyToValidate { get; set; }
public bool PropertyValidationDependsOn { get; set; }
}
Conditionally Remove Fields from the Model State in the Controller
In the end, this is the solution I went with. The controller executes this code when the form is posted:
if (Request.IsAuthenticated)
{
...
// We don't need to validate user fields if user is logged in.
ModelState.Remove("CommenterName");
ModelState.Remove("Email");
}
By removing the fields from the model state, it won’t be invalid when they are missing. This method is simple and avoids adding additional dependencies.