Stop SOPA and PIPA


Please do not allow SOPA, PIPA, or any forms of Internet censorship or regulation to get through Congress.

Over the past several years I've noticed a trend in our government where we've shifted away from laws protecting citizens from corporations and government to laws protecting corporations and government from citizens. This is coming at the cost of our liberty and pursuit of happiness.

By preventing SOPA, PIPA, or other Internet censorship/regulation, you can help reverse this trend.

Thank you,
Jeff Handley

 

I am submitting this text to my senate and house representatives, and I’m also making all pages on this blog redirect a PSA page until 1/19/2012 as part of my effort to stop SOPA and PIPA.

author: Jeff Handley | posted @ Tuesday, January 17, 2012 10:10 PM | Feedback (1)

Merry Christmas! It’s my off-site backup.


Almost 4 years ago, I moved my family from Cincinnati, Ohio to Redmond, Washington so that I could join Microsoft.  Since we have two young children, and a small family, the move has been really hard on the grandparents.  But as our first Christmas out here approached, I came up with an idea that I’ve been repeating each year since.  Last night, I tweeted about this idea and the tweet went a bit viral, with hundreds of retweets (thanks largely to Scott Hanselman, I’m sure).

Like many families, we take thousands of digital photos and we store them on an HP MediaSmart HomeServer.  Every year for the last 4 years, I sit down the weekend before Christmas, copy all of the year’s pictures from the HomeServer to my PC, organize them into folders of 4GB, burn them to DVD, and then send them to the grandparents as my Christmas gift to them.  What they don’t know (until now anyway), is that the gesture isn’t completely selfless.  As succinctly expressed in my tweet, those disks are my off-site backup of our family photos.  And we’re talking 2300 miles off-site, so I consider myself pretty well covered.

Here are a few details for my process, in case you decide to pick up this routine too:

  1. Organize the photos into folders with the year, subfolders with the year-month, then subfolders again with year-month-day, and file names that have date-time stamps.  (I store these on our HomeServer).
  2. When copying the folders in preparation for the year’s disks, always include December from the previous year, so that photos from the previous Christmas are included in the set.
  3. Split the photos into groups of ~4GB, and burn the groups to disks.  This year I had 7 DVDs.
  4. Deliver the disks to the grandparents, and ideally copy the photos to their PC and add them to the screensaver.

My wife and I have agreed many times that in the case of a disaster, our family photos are what we’d miss the most.  Having all of our photos archived at the grandparents’ offers us a bit of reassurance that we’ll never lose them.  And you cannot imagine how much joy the grandparents get when they watch the photo slideshow.  We’ve spent several hours sitting together watching the screensaver.

In the days of Angry Birds, this is what I call killing two stones with one bird.  Merry Christmas!

author: Jeff Handley | posted @ Monday, December 19, 2011 12:26 AM | Feedback (2)

WCF RIA Services V1.0 SP2 Released


On December 9, 2011, Silverlight 5 was released.  As always, RIA Services is included in the Tools for Silverlight, and this new release included our V1.0 SP2 final build.  We refer to this as our RTW build, meaning Released-To-Web.  In addition to the Tools installer including WCF RIA Services V1.0 SP2, you can also find the standalone installer on Microsoft Download Center.  The silverlight.net/getstarted/riaservices page was also updated to reflect the new release.

Aside from some bug fixes here and there, not much changed between SP2 RC and SP2 RTW.  The primary focus as been ensuring really solid compatibility with both Silverlight 5 and Silverlight 4 with this release, and testing the bits on tons of configurations for both design-time and runtime.  If you are upgrading from V1.0 SP1 to V1.0 SP2 though, here’s what you’ll find in this release:

  1. Support for both Silverlight 4 and Silverlight 5 (SP1 had subtle issues that would cause bugs in SL5 apps)
  2. DateTimeOffset support
  3. Entity Framework Code-First development, using this NuGet package

There are some more details in my SP2 RC announcement.

Known issues include:

  1. Entity Framework 4.1 is required, but version 4.2 isn’t supported yet
  2. Requires Visual Studio 2010 or SP1. Visual Studio 11 isn’t supported yet
  3. Work on supporting Universal Providers for improved Windows Azure deployment is ongoing
  4. If you want to create a Silverlight 4 Business Application Template, first create an instance of the Silverlight 5 template, and then use the Silverlight project properties to retarget it to Silverlight 4. You might see some temporary errors (System.Windows.Markup.XamlParseException) in the designer and error window, but rebuilding will make those disappear.

I want to send a shout out to Yavor Georgiev and point you to his blog, as he has been driving this SP2 RTW release for the last couple of months.  I’ve transitioned off of the WCF RIA Services team and over to ASP.NET Web Pages, NuGet, and Razor.

If you have any questions or issues with the SP2 release, jump over to the forums.

Technorati Tags: ,

author: Jeff Handley | posted @ Monday, December 12, 2011 11:04 PM | Feedback (2)

GitHub, Triggered Deployments, and In-Browser Editing


Earlier this month, I explored using WebMatrix, Git, and AppHarbor to quickly create and publish a web site in the cloud.  While at work this afternoon I got a phone call from a relative with a usability issue on the site.  To fix the issue, I simply needed to wrap a hyperlink around an image, but I only had about 60 seconds to spare, which wasn’t enough time.  Here’s what I would have needed to do:

  1. Open up a command prompt and cd into D:\github
  2. git clone https://jeffhandley@appharbor.com/xanderhandley.git
  3. Open the offending cshtml file, make the edit
  4. git commit –am "Made the popcorn site image a hyperlink"
  5. git push https://jeffhandley@appharbor.com/xanderhandley.git

Okay, so in retrospect it would have only taken about 120 seconds to do, but at that moment, I didn’t think I could do it in the minute I had to spare.  I am going to use this opportunity to introduce GitHub into my workflow and be prepared for these kinds of adjustments should they arise again.

GitHub

I will describe GitHub by quoting their own site:

GitHub is the best way to collaborate with others. Fork, send pull requests and manage all your public and private git repositories.

It’s also a nice and free place to clone my repositories in the cloud so that I don’t have to worry about backups and whatnot.  And honestly, it’s kind of a “central” repository for distributed version control. Don’t hate–you know you think of it that way too.

When I first created and published my son’s popcorn site though, GitHub wasn’t needed in the workflow.  Instead, I just went directly from my PC to the cloud.  But with today’s episode, I needed to be able to get the latest version of the code on my work computer.  I wanted a central repository, and starting tonight, GitHub will serve that purpose.  Here’s how:

  1. Go to http://github.com and sign up / log in
  2. Create a New Repository
  3. On my home PC, where my existing repository is for the project, open a command window
  4. git remote add origin https://jeffhandley@github.com/jeffhandley/XanderHandley.git
  5. git push –u origin master

I now have my repository cloned up in GitHub and I can push changes to it.  But just like with regular expressions, now I have two problems.  When I want to deploy an update to the website, I need to get the updates to both GitHub and AppHarbor.

Triggered Deployments

Fortunately, GitHub has a feature that turns the two problems (of pushing to both GitHub and AppHarbor) back into 1 problem.  I can configure a Service Hook in GitHub such that any time I push an update to GitHub, the changes are also automatically pushed to AppHarbor.

Here’s how to do it:

  1. On GitHub, while viewing the files for your repository, click on the “Admin” button at the top
  2. Click on “Service Hooks” on the left
  3. Select AppHarbor
  4. Open a new tab in your browser and go load up your AppHarbor application
  5. Under where AppHarbor shows your Repository URL, you’ll see a link next to “Create build URL” – click that to see your build URL
  6. Paste the Application Slug and Token into the AppHarbor Service Hooks configuration in GitHub as they explain, make the hook Active, and click Update Settings

The GitHub repository is now set up so that any push into my master branch on GitHub will also push to AppHarbor.  To verify this, I just went through the following steps:

  1. Opened the website on my home PC
  2. Made the change, adding the hyperlink
  3. Opened a command prompt where the website files are
  4. git commit –am "Made the popcorn site image a hyperlink"
  5. git push origin master

I then refreshed github and saw the commit listed there.  Then I went over to AppHarbor and refreshed and saw the commit there too.  Then I went to http://xanderhandley.com/popcorn and refreshed and saw the new hyperlink.

In-Browser Editing

With all of this magic awesomeness, I was thinking it couldn’t be any easier to edit Xander’s website.  But sure it can.  Now that I have GitHub and AppHarbor connected, I can utilize another goodie that GitHub has to offer: in-browser editing.  GitHub allows you to edit files directly in the browser; just browse to a file and click on the “Edit this file” button.  You’ll get a text editor in the browser, along with a commit message box and a “Commit Changes” button.  That’s right, you can make single-file edits directly in the browser and commit them to your repository in GitHub.  After committing, the service hook we configured will be executed, and the change will be deployed to AppHarbor immediately.

I just exercised this capability by editing default.cshtml, adding a “This page intentionally left blank” message to it, and committing the change.  Within seconds, that change was deployed to xanderhandley.com.  I now have an in-browser editor of my son’s website such that any changes I commit are immediately deployed to the cloud.  This certainly isn’t a routine I’d use for a business website or anything remotely complicated, but for my son’s website, this is perfect.  And it’s free.

Technorati Tags: ,,

author: Jeff Handley | posted @ Tuesday, October 25, 2011 12:01 AM | Feedback (0)

WebMatrix, Git, and AppHarbor


I needed to crank out a single-page web site for my son, to help him promote his popcorn sales for cub scouts.  I decided it was a perfect opportunity to try some new things, so I jumped into WebMatrix to create the site, added it to a Git repository, and deployed it to AppHarbor under Xander’s own domain name.  Start to finish, it took 30 minutes and my son gave me an A+.

Install WebMatrix 2 Beta NowInstalling WebMatrix

WebMatrix is one of the easiest-to-install Microsoft products ever.  Really.  Just go to http://www.microsoft.com/web/webmatrix/ and click the big green button for “Install WebMatrix” or “Or try the latest Beta.”  I decided to try the latest beta, and after just a couple more simple clicks and about 4 minutes, WebMatrix 2 Beta was on my machine and open.

Creating the Web Site

I clicked on Templates and since this is going to be an extremely simple, one-page site, I went for the “Empty Site” template.  The Personal Site template was tempting, but over-the-top for my needs.  WebMatrix created the site in moments and then gave me a nice start page.  I immediately jumped to the “Files” view and here’s what I saw I was given:

image

I created a new Popcorn.cshtml file that contains the content my son helped author.  It was a no-brainer to use the CSHTML file, as that lets me use the awesome Razor syntax as needed.  I created an /images folder and a /videos folder, where I put some media I had prepared.  Then I hit the Run button.  Yes, I prefer Chrome.

image

Since I had added my content to Popcorn.cshtml, and hadn’t added anything to the Default.cshtml, I got a blank page.  That’s good with me, as a default page is out of scope for tonight’s project. :-)  What I really wanted to see is what I got when I browsed to /popcorn…

image

Perfect!  Notice that I didn’t have to browse to popcorn.cshtml – just /popcorn is sufficient because ASP.NET Web Pages automatically routes this form me.  That feels so much cleaner than creating a popcorn folder and adding a default.cshtml to it.  So, the website is now complete and ready for source control.

Installing Git

github has an awesome page for setting up Git.  There’s no need for me to repeat anything here.  Just go to github’s page and then come back here when you’re ready.

Creating the Git Repository

This was easy stuff.  Using Git Bash, here’s what it took to initialize the Git repository, add the website, review the pending changes, and commit them.

image

Deploying to AppHarbor

If you’re not familiar with AppHarbor, it’s a service that lets you host sites using Git to push a repository to the cloud (no, it’s not Azure – shame).  AppHarbor will host this site for free, which is a requirement for this project.  Signing up for AppHarbor was extremely simple and took about 1 minute.

Before I could deploy, I needed to first create an application.  It’s a single-field form.  Seriously.

image

After naming the application and clicking Create, I was given idiot-proof instructions for how to set up a Git repository, configure it to push to AppHarbor, and do my first push.  Since I already initialized the repository, I skipped down to the “Add AppHarbor Repository and Push” section.

image

At that point, the site was deployed to AppHarbor and running in the cloud.  Browsing to http://xanderhandley.apphb.com/popcorn/ worked. I was close to finished; I just needed to set up the custom hostname.

Using a Custom Hostname

I wanted the site to have an easy URL, with http://xanderhandley.com/popcorn as the goal.  Needless to say, I already own xanderhandley.com, so I just needed to point it to AppHarbor.  This was another single-field form on AppHarbor’s site, reached by clicking on the “Hostnames” menu link.

image

Before hitting ‘Add hostname,’ I copied the IP address to my clipboard.  Then I went off to my DNS settings for the domain and configured an A record for the domain to point to that IP address.  After a few minutes of propagation, it worked.  http://xanderhandley.com/popcorn loads the site I created in WebMatrix just 30 minutes ago, and Xander’s now able to sell popcorn online!

Technorati Tags: ,,

author: Jeff Handley | posted @ Saturday, October 08, 2011 11:25 PM | Feedback (2)

RIA Services updates for //build


At //build, Dinesh Kulkarni from our team gave a presentation alongside Asad Khan from the OData team.  They showed RIA/JS consuming both a RIA Services DomainService as well as an OData feed from Azure.  Dinesh also showed RIA/JS working in the browser, in a Win8 Metro Style app, and on an iPhone using PhoneGap.  You can watch their talk on Channel 9.

To coincide with their talk, the RIA Services team published updates for RIA/JS:

  • Updated RIAServices.jQuery NuGet package
  • New RIAServices.jQuery.Sample NuGet package
  • WCF RIA Services Toolkit (September 2011)
  • New RIA/JS landing page: http://win8.ms/riajs

RIAServices.jQuery

We are favoring NuGet over the MSI for toolkit components, as we can update the packages independently and more quickly.

  • $.observable for data editing / change events – This is a prototype implementation of an observability pattern we’re working with the jQuery and jQuery UI teams on, following the pattern we’ve established with $.dataSource.
  • Query data from OData services – We’re now able to load / query / mash-up data from OData sources.
  • $.dataSource IntelliSense – You’ll now find detailed API guidance from the $.dataSource IntelliSense
  • Simpler packaging / JavaScript classes/namespaces – RIA/JS now only has a single JavaScript file: ria.js.  It contains classes like DataSource, DataContext, et al, and we’ve refined how we define classes and namespaces to follow common jQuery patterns.

RIAServices.jQuery.Sample

Following the lead of the Entity Framework team, we’ve created a NuGet package that adds sample code into your project for using RIA/JS.  This is a great way to temporarily add sample code into your project, learn from it, and then uninstall it once you’ve implemented your own scenarios.  Give it a try!

WCF RIA Services Toolkit (September 2011)

The Toolkit has been updated to have the same content as the RIAServices.jQuery package.  There were no other updates to the Toolkit in this release.  You can always find the latest toolkit here: http://jeffh.me/riatoolkit.

This Toolkit build works with both WCF RIA Services V1.0 SP1 and the SP2.

Version Compatibility

With all of these new releases flying all around, it’s hard to know what works where. Hopefully this helps:

Release Silverlight 4 Silverlight 5 RIA/JS Visual Studio 11 Developer Preview
WCF RIA Services V1.0 (RTM) Yes No No No
WCF RIA Services V1.0 SP1 Yes No No No
WCF RIA Services V1.0 SP2 RC Yes Yes Yes No
WCF RIA Services Toolkit (September 2011) Yes Yes Yes No

At the time of WCF RIA Services V1.0 SP2 RC, Visual Studio 11 is not yet supported.  This is mostly because of our design-time components, and we are working to get that in place to offer VS11 support as soon as possible--stay tuned for updates therein.

As you can see above, if you are building Silverlight 4 applications, you can use RIA Services V1.0 SP1 or SP2.  If you are developing Silverlight 5 applications though, you must use RIA Services V1.0 SP2.

Technorati Tags: ,,

author: Jeff Handley | posted @ Friday, September 16, 2011 6:07 PM | Feedback (4)

RIA Services Validation: Available on GitHub


I’ve gotten quite a few requests for the source code behind the RIA Services Validation blog post series.  I am pleased to announce that the code is now published on GitHub.

http://github.com/jeffhandley/RIAServicesValidation

Within that repository, you’ll find:

  1. RudeValidation.Web/Models – Server-side model classes
  2. RudeValidation.Web/Validators – Custom validators including CompareValidator, ConditionallyRequired, and DateValidator
  3. RudeValidation/Views – Sample screens showing the validation in action
  4. RudeValidation/ViewModels - Where you’ll find the sample ViewModel as well as the very simple ViewModelBase

If you’re not familiar with Git, don’t be intimidated.  You can easily view the code directly in your browser by starting out at http://github.com/jeffhandley/RIAServicesValidation and just clicking through the folders and files.  To download all of the source, just click the Downloads button, and click to download the Zip file.  As always when downloading a Zip from the tubes, be sure to Unblock the Zip file through the Properties window before you extract the contents.  Then, be sure to set the RudeValidation.Web project as the startup.  After that, you are welcome to do whatever you want with the source code included.

RIA Services Validation Recap

Over the course of a 10-post series, we learned about the standard validators, how to create different types of custom validators, how the validation attributes get propagated, how to perform common cross-field and cross-entity validation, and now we have a utility for importing model rules into ViewModel classes.  Here’s the full series:

    1. Standard Validators
    2. Custom Validation Methods
    3. Custom Reusable Validators
    4. Attribute Propagation
    5. Validation Triggers
    6. Cross-Field Validation
    7. Entity-Level Validation
    8. Providing ValidationContext
    9. Using ValidationContext (Cross-Entity Validation)
    10. ViewModel Validation with Entity Rules

author: Jeff Handley | posted @ Tuesday, September 06, 2011 11:50 PM | Feedback (2)

RIA Services Validation: ViewModel Validation with Entity Rules


For those of you familiar with the ViewModel (or MVVM) pattern, you are likely also familiar with a typical pain point regarding validation: you often need to duplicate your entity validation metadata onto your ViewModel classes.  This can lead to burdensome dual maintenance of your validation rules, and it can seem very frustrating that with the server to client metadata propagation that RIA Services offers, your ViewModel classes are left dangling out there for you to manage yourself.  In this post, I’ll illustrate a utility I created that allows a ViewModel to assume validation metadata from model classes or properties, eliminating the dual maintenance.

ModelPropertyValidator

Virtually every ViewModel will have properties that actually represent properties from your model.  Let’s use the meeting invitation model from earlier RIA Services Validation posts as an example.  My meeting object has a bunch of properties such as Title, Start, End, Location, MinimumAttendees, MaximumAttendees, and Details.  It’s easy to imagine a ViewModel that could be used for managing meetings where the UI would have a subset or superset of the meeting properties.  For each field on the screen that represents a meeting property, I should be able to indicate which model property is associated with each ViewModel property so that the model validation can be imported automatically.  This is where ModelPropertyValidator comes in.

Here’s a subset of the Meeting class with its data annotations in my Web project:

Code Snippet
  1. [CustomValidation(typeof(MeetingValidators), "PreventExpensiveMeetings")]
  2. [CustomValidation(typeof(MeetingValidators), "PreventDoubleBooking")]
  3. [MetadataType(typeof(Meeting.MeetingMetadata))]
  4. public partial class Meeting
  5. {
  6.     public class MeetingMetadata
  7.     {
  8.         [Key]
  9.         [Display(AutoGenerateField = false)]
  10.         public int MeetingId { get; set; }
  11.  
  12.         [Required]
  13.         [CustomValidation(typeof(MeetingValidators), "NoEarlyMeetings")]
  14.         [CompareValidator(CompareOperator.LessThan, "End",
  15.             ErrorMessage = "Meetings cannot result in time travel.")]
  16.         [DateValidator(DateValidatorType.Future)]
  17.         [Display(Order = 1)]
  18.         public DateTime Start { get; set; }
  19.  
  20.         [Required]
  21.         [CompareValidator(CompareOperator.GreaterThan, "Start",
  22.             ErrorMessage = "Meetings cannot result in time travel.")]
  23.         [Display(Order = 2)]
  24.         public DateTime End { get; set; }
  25.  
  26.         [Required]
  27.         [StringLength(80, MinimumLength = 5,
  28.             ErrorMessageResourceType = typeof(ValidationErrorResources),
  29.             ErrorMessageResourceName = "TitleStringLengthErrorMessage")]
  30.         // {0} must be at least {2} characters and no more than {1}.
  31.         [Display(Order = 0)]
  32.         public string Title { get; set; }

 

When a ViewModel is going to expose the Start property for a meeting, it would be a nightmare to have to keep all of the validation attributes in sync.  To alleviate this, I created an attribute called ModelPropertyValidatorAttribute that acts as a proxy from a ViewModel property to a Model property.  Here’s what it looks like to use it:

Code Snippet
  1. public class MeetingEntryViewModel : ViewModelBase
  2. {
  3.     private DateTime start;
  4.     private DateTime end;
  5.  
  6.     [ModelPropertyValidator(typeof(Meeting))]
  7.     public DateTime Start
  8.     {
  9.         get { return this.start; }
  10.         set
  11.         {
  12.             if (this.start != value)
  13.             {
  14.                 this.ValidateProperty("Start", value);
  15.                 this.start = value;
  16.                 this.RaisePropertyChanged("Start");
  17.             }
  18.         }
  19.     }
  20.  
  21.     [ModelPropertyValidator(typeof(Meeting))]
  22.     public DateTime End
  23.     {
  24.         get { return this.end; }
  25.         set
  26.         {
  27.             if (this.end != value)
  28.             {
  29.                 this.ValidateProperty("End", value);
  30.                 this.end = value;
  31.                 this.RaisePropertyChanged("End");
  32.             }
  33.         }
  34.     }
  35. }

 

With a simple attribute on each, the Start and End properties now import all of the validation attributes from their corresponding Model properties on the Meeting type; no more validation duplication!  When the property name is the same on the Model and the ViewModel, you can get away with only specifying the model type, but I also have an overload that accepts the model property name in case the property names differ.

ModelObjectValidator

Just as there are cases when you want a ViewModel property to import the validation attributes from a model property, there are also times when you want a ViewModel class to import a model’s object-level validation attributes.  That’s where ModelObjectValidatorAttribute comes in.  Very similarly to the ModelPropertyValidator, you just put the attribute on the ViewModel and specify the corresponding model type.

Code Snippet
  1. [ModelObjectValidator(typeof(Meeting))]
  2. public class MeetingEntryViewModel : ViewModelBase, IStartAndEnd
  3. {
  4.     private DateTime start;
  5.     private DateTime end;

 

That’s all there is to it.  Now the MeetingEntryViewModel class imports the object-level validation from the Meeting type.  You can even specify multiple ModelObjectValidator attributes if you want to import validation attributes from multiple classes.

How’d you do that?

Here’s how it works:

  1. ModelPropertyValidatorAttribute and ModelObjectValidatorAttribute both derive from ModelValidatorAttribute
  2. ModelValidatorAttribute is an abstract class that derives from ValidationAttribute
  3. ModelValidatorAttribute overrides IsValid and performs much of the same work that Validator performs, iterating over the attributes on the target and producing validation errors as ValidationResult instances
  4. If the ViewModel derives from the RIA Services ComplexObject class, then there’s a nice ValidationErrors property that can be used to add multiple error messages
  5. Otherwise, the first validation error is returned as the error message from this custom validator

Yes, I suggested that you make your ViewModel classes derive from ComplexObject.  ComplexObject is a really useful class and it exposes a great ValidationErrors collection that any consumer can manipulate, and all updates to that collection generate events from the INotifyDataErrorInfo interface.  This works perfectly for this scenario where I want my validation attribute to be able to push multiple validation results onto the target object, despite the fact that the IsValid method is only capable of returning a single error.

Here’s the full code for the ModelValidatorAttribute and the two derived forms.

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel.DataAnnotations;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.ServiceModel.DomainServices.Client;
  7.  
  8. namespace RudeValidation.Helpers
  9. {
  10.     public class ModelPropertyValidatorAttribute : ModelValidatorAttribute
  11.     {
  12.         public ModelPropertyValidatorAttribute(Type modelType)
  13.             : base(modelType, ModelValidationMode.InferProperty) { }
  14.  
  15.         public ModelPropertyValidatorAttribute(Type modelType, string propertyName)
  16.             : base(modelType, propertyName) { }
  17.     }
  18.  
  19.     public class ModelObjectValidatorAttribute : ModelValidatorAttribute
  20.     {
  21.         public ModelObjectValidatorAttribute(Type modelType)
  22.             : base(modelType, ModelValidationMode.Object) { }
  23.     }
  24.  
  25.     [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = true)]
  26.     public abstract class ModelValidatorAttribute : ValidationAttribute
  27.     {
  28.         public Type ModelType { get; private set; }
  29.         public ModelValidationMode ValidationMode { get; private set; }
  30.         public string ModelProperty { get; private set; }
  31.  
  32.         private object model;
  33.  
  34.         public ModelValidatorAttribute(Type modelType, ModelValidationMode validationMode)
  35.         {
  36.             this.ModelType = modelType;
  37.             this.ValidationMode = validationMode;
  38.         }
  39.  
  40.         public ModelValidatorAttribute(Type modelType, string modelPropertyName)
  41.         {
  42.             this.ModelType = modelType;
  43.             this.ValidationMode = Helpers.ModelValidationMode.SpecifiedProperty;
  44.             this.ModelProperty = modelPropertyName;
  45.         }
  46.  
  47.         protected override ValidationResult IsValid(object value, ValidationContext validationContext)
  48.         {
  49.             if (model == null)
  50.             {
  51.                 model = Activator.CreateInstance(this.ModelType);
  52.             }
  53.  
  54.             ValidationContext redirectedContext = new ValidationContext(model, validationContext, validationContext.Items);
  55.  
  56.             switch (this.ValidationMode)
  57.             {
  58.                 case ModelValidationMode.InferProperty:
  59.                     redirectedContext.MemberName = validationContext.MemberName;
  60.                     break;
  61.                 case ModelValidationMode.SpecifiedProperty:
  62.                     redirectedContext.MemberName = this.ModelProperty;
  63.                     break;
  64.                 case ModelValidationMode.Object:
  65.                     redirectedContext.MemberName = null;
  66.                     break;
  67.             }
  68.  
  69.             ComplexObject targetEntity = validationContext.ObjectInstance as ComplexObject;
  70.             var breakOnFirstError = (targetEntity == null);
  71.             IEnumerable<ValidationResult> validationResults = TryValidateProperty(value, validationContext, redirectedContext, breakOnFirstError);
  72.  
  73.             if (validationResults.Any())
  74.             {
  75.                 if (validationResults.Count() == 1)
  76.                 {
  77.                     return validationResults.Single();
  78.                 }
  79.  
  80.                 if (targetEntity != null)
  81.                 {
  82.                     foreach (ValidationResult result in validationResults.Skip(1))
  83.                     {
  84.                         targetEntity.ValidationErrors.Add(result);
  85.                     }
  86.                 }
  87.  
  88.                 return validationResults.First();
  89.             }
  90.  
  91.             return ValidationResult.Success;
  92.         }
  93.  
  94.         private static IEnumerable<ValidationResult> TryValidateProperty(object value, ValidationContext validationContext, ValidationContext modelValidationContext, bool breakOnFirstError)
  95.         {
  96.             ICustomAttributeProvider validatorProvider;
  97.  
  98.             if (!string.IsNullOrEmpty(modelValidationContext.MemberName))
  99.             {
  100.                 validatorProvider = modelValidationContext.ObjectType.GetProperty(modelValidationContext.MemberName);
  101.             }
  102.             else
  103.             {
  104.                 validatorProvider = modelValidationContext.ObjectType;
  105.             }
  106.  
  107.             IEnumerable<ValidationAttribute> validators = validatorProvider
  108.                 .GetCustomAttributes(typeof(ValidationAttribute), true)
  109.                 .Cast<ValidationAttribute>();
  110.  
  111.             IEnumerable<ValidationResult> results = GetValidationErrors(value, validationContext, validators, breakOnFirstError);
  112.             return results;
  113.         }
  114.  
  115.         private static IEnumerable<ValidationResult> GetValidationErrors(object value, ValidationContext validationContext, IEnumerable<ValidationAttribute> attributes, bool breakOnFirstError)
  116.         {
  117.             List<ValidationResult> errors = new List<ValidationResult>();
  118.             bool hasErrors = false;
  119.             ValidationResult result;
  120.  
  121.             foreach (RequiredAttribute required in attributes.OfType<RequiredAttribute>())
  122.             {
  123.                 result = required.GetValidationResult(value, validationContext);
  124.  
  125.                 if (result != ValidationResult.Success)
  126.                 {
  127.                     errors.Add(result);
  128.                     hasErrors = true;
  129.  
  130.                     if (breakOnFirstError)
  131.                     {
  132.                         return errors;
  133.                     }
  134.                 }
  135.             }
  136.  
  137.             if (hasErrors)
  138.             {
  139.                 return errors;
  140.             }
  141.  
  142.             foreach (ValidationAttribute attribute in attributes)
  143.             {
  144.                 if (attribute is RequiredAttribute)
  145.                 {
  146.                     continue;
  147.                 }
  148.  
  149.                 result = attribute.GetValidationResult(value, validationContext);
  150.  
  151.                 if (result != ValidationResult.Success)
  152.                 {
  153.                     errors.Add(result);
  154.                     hasErrors = true;
  155.  
  156.                     if (breakOnFirstError)
  157.                     {
  158.                         return errors;
  159.                     }
  160.                 }
  161.             }
  162.  
  163.             return errors;
  164.         }
  165.     }
  166.  
  167.     public enum ModelValidationMode
  168.     {
  169.         InferProperty,
  170.         SpecifiedProperty,
  171.         Object
  172.     }
  173. }

 

RIA Services Validation Recap

This was the 10th installment in a blog post series about RIA Services Validation.  We’ve learned about the standard validators, how to create different types of custom validators, how the validation attributes get propagated, how to perform common cross-field and cross-entity validation, and now we have a utility for importing model rules into ViewModel classes.  Here’s the full series:

    1. Standard Validators
    2. Custom Validation Methods
    3. Custom Reusable Validators
    4. Attribute Propagation
    5. Validation Triggers
    6. Cross-Field Validation
    7. Entity-Level Validation
    8. Providing ValidationContext
    9. Using ValidationContext (Cross-Entity Validation)
    10. ViewModel Validation with Entity Rules

The source code for everything shown during the series is available on GitHub.

author: Jeff Handley | posted @ Tuesday, September 06, 2011 11:31 PM | Feedback (0)

Business Application Project Login / Registration Disables Everything


There is a known issue with WCF RIA Services V1.0 SP2 RC’s Business Application project template.  The issue only surfaces in Silverlight 5, and there’s an easy way to fix it.  We determined the root cause of this issue after it was too late to fix it in time for the RC release, but the correction is in place for the RTW (final) release of SP2.

This bug surfaces after you create a new project after installing RIA Services SP2, using the Business Application project template, and then an end user registers or logs in.  After completing the registration/login process, the entire application is disabled.  You can see the Home, About, and logout buttons are disabled, and nothing is clickable.

image

If you change the project to target Silverlight 4, the problem goes away.  This made us think the problem was in the Silverlight runtime or SDK, but after the Silverlight team investigated it, the root cause ended up being in the project template within the code we put in place to help you get started.

It boils down to the LoginRegistrationWindow.xaml.cs file that is included in the template under the Views\Login folder in the Silverlight project.  Within that file, the OnOpened method from ChildWindow is overridden:

Code Snippet
  1. /// <summary>
  2. /// Ensures the visual state and focus are correct when the window is opened.
  3. /// </summary>
  4. protected override void OnOpened()
  5. {
  6.     this.NavigateToLogin();
  7. }

 

The problem is that in this override, the code does not call base.OnOpened().  When targeting the Silverlight 4 SDK, this just happens to work out okay and the screen gets re-enabled, but when targeting the Silverlight 5 SDK, a change to ChildWindow makes this misuse lead to a disabled screen.  By simply adding base.OnOpened before this.NavigateToLogin(), the problem goes away.

Code Snippet
  1. /// <summary>
  2. /// Ensures the visual state and focus are correct when the window is opened.
  3. /// </summary>
  4. protected override void OnOpened()
  5. {
  6.     base.OnOpened();
  7.     this.NavigateToLogin();
  8. }

 

With that fix in place, you can register/log in and the app will return to normal.  This same problem exists for the VB version of the template. You would need to a call to MyBase.OnOpened() to fix the problem.  Here’s what the Home, About, and logout links will look after applying the fix.

image

Technorati Tags: ,

author: Jeff Handley | posted @ Saturday, September 03, 2011 3:42 PM | Feedback (2)

WCF RIA Services V1.0 SP2 RC


On September 1, 2011, Silverlight 5 RC was released.  As always, RIA Services is included in the Tools for Silverlight, and that release included our V1.0 SP2 RC build.  And also as always, we published a standalone installer for RIA Services.  You can find it on the download center.  The silverlight.net/getstarted/riaservices page will get updated soon to include the link to this build.

This release is one more step toward closing down our SP2 release, and we fixed a number of bugs since SP1.  Some notable differences between SP1 and SP2 are:

  1. DateTimeOffset is supported in SP2
  2. Support for Silverlight 5 (see below)
  3. Entity Framework Code-First Support (see below)
  4. Live IntelliSense is deprecated
  5. OperationTag was removed

Support for Silverlight 5 (and Silverlight 4)

If you’re using RIA Services SP1, you can target Silverlight 5 with your applications and everything will seem to work.  Unfortunately, there’s a small issue that is lurking in your generated code, ready and waiting to cause you bugs.  If you look closely at differences between SL4 and SL5 code, you’ll see that there RIA Services SP1 never generates a [RoundtripOriginal] attribute.  The omission of this attribute can wreak havoc on your application, and it’s unfortunate that it is silently omitted.  With RIA Services SP2, the attribute will return when you are targeting Silverlight 5, and your app will be back to normal.

This issue was introduced because Silverlight 5 changed the runtime and SDK assembly versions from 2.0.5.0 to 5.0.5.0.  Deep down in the RIA Services code generation layer, this caused problems where Silverlight assemblies with the 2.0.5.0 version on them could not be loaded.  Ultimately, that surfaced as attributes from those assemblies not getting propagated from the server to the client.  Because RoundtripOriginalAttribute is defined in a SL4-based assembly, it fails to get generated when targeting Silverlight 5.

We fixed this issue in RIA Services SP2, so if you are targeting Silverlight 5, you must use RIA Services SP2.

In addition to using RIA Services SP2 for Silverlight 5 applications, you can still target Silverlight 4 as well.  RIA Services does not depend on Silverlight 5 at all and is in fact still compiled against Silverlight 4 to ensure backward-compatibility.

Entity Framework Code First Support

Support for Entity Framework Code First is provided through either the RIA Services Toolkit, or through the RIAServices.EntityFramework NuGet package.  Rather than repeating myself, I’ll just point you to some previous posts on this topic:

The difference that you’ll see with the SP2 RC release though is that EF Code First DbContext classes are now respected by the Domain Service Wizard when using “Add New Item… Domain Service.”  When you have a valid DbContext class, and you have the Microsoft.ServiceModel.DomainServices.EntityFramework assembly referenced (either from the Toolkit or NuGet), you can now select it from the drop-down and have your Domain Service class stubbed out for you.

For more info on how to use EF Code First with RIA Services, please check out Varun Puranik’s blog.  But again, with SP2 RC, you’ll get Domain Service wizard support. Yeah!  But don’t forget you need to add the reference to the Toolkit assembly or (preferably) the RIAServices.EntityFramework NuGet package in order for it to light up.

Side by Side Support?

Some people have asked me if RIA Services supports Side-by-Side installations.  No, it doesn’t.  If you’re targeting Silverlight 4 and you need a Go-Live license, use RIA Services V1.0 SP1.  If you’re targeting Silverlight 5 or you don’t need a Go-Live license, then use RIA Services V1.0 SP2.

Something else I wanted to mention regarding side-by-side support is Silverlight 4 and Silverlight 5.  There’s an important detail that I don’t know has been communicated properly: When installing Silverlight 5 on your machine, you must keep the Silverlight 4 SDK and Tools installed on your machine.  For that matter, you need to keep the Silverlight 3 SDK installed too.  If you uninstall any of those things, I promise you pain.  Installing Silverlight 5 RC Tools will give you the Silverlight 5 runtime, SDK, and Tools.  Your Silverlight 3/4 SDK/Tools will be left in place as these function side-by-side.  Your Silverlight runtime will be replaced with SL5, but that won’t interrupt SL4 work.

Roadmap

We are getting close to a final release of SP2.  You can expect our final release to be included with Silverlight 5’s final release.  We are listening for feedback on the RC though, and we look forward to getting the RTW build in your hands soon!

author: Jeff Handley | posted @ Friday, September 02, 2011 6:25 PM | Feedback (10)