28 Jun 2012

Access Denied on the Amazon MWS Scratchpad

Annoying! I was getting an Access Denied error when sending through requests on the Amazon MWS Scratchpad. I checked and rechecked I was sending through the correct Access Key, Secret Key, Marketplace ID etc.

Turns out I was on the wrong site! My Marketplace was in the UK so I have to use

https://mws.amazonservices.co.uk/scratchpad/index.html

NOT

https://mws.amazonservices.com/scratchpad/index.html

20 Jun 2012

A custom ValueInjecter Injection to use with models decorated with the MVC Bind Attribute

In some cases, I would like to use the Omu ValueInjecter's InjectFrom() to only copy source properties which have been marked for inclusion by an MVC BindAttribute. So, I've developed the following Injection:

public class BindAwareInjection : LoopValueInjection
{
    public List<string> PropertiesToInclude = new List<string>();
    public List<string> PropertiesToExclude = new List<string>();
    public BindRuleLocationType BindRuleLocation = BindRuleLocationType.Source;

    public enum BindRuleLocationType
    {
        Source,
        Target
    }

    protected override void Inject(object source, object target)
    {
        var bindRuleObject = BindRuleLocation == BindRuleLocationType.Source ? source : target;

        var bindAttributes = (BindAttribute[])bindRuleObject.GetType().GetCustomAttributes(typeof(BindAttribute), true);
        foreach (var bindRule in bindAttributes)
        {
            if (!string.IsNullOrEmpty(bindRule.Include))
            {
                PropertiesToInclude = new List<string>(bindRule.Include.Split(','));
            }
            else if (!string.IsNullOrEmpty(bindRule.Exclude))
            {
                PropertiesToExclude.AddRange(bindRule.Exclude.Split(','));
            }
        }

        base.Inject(source, target);
    }

    protected override bool UseSourceProp(string sourcePropName)
    {
        if (PropertiesToInclude.Count > 0)
        {
            return PropertiesToExclude.Any(prop => prop == sourcePropName);
        }
        else if (!PropertiesToExclude.Any(prop => prop == sourcePropName))
        {
            return true;
        }
        return false;
    }
}

This allows me to simply define ViewModels with Bind Exclusion lists like this:

public class User{
    public bool IsSuperUser { get; set; }
    public string Name { get; set; }
}

[Bind(Exclude = "IsSuperUser")]
public class UserVM : User { }

And then my Controller's Update Action would look like this:

[HttpPost]
public ActionResult UpdateUser(UserVM model)
{
    User target = Repository.GetCurrentUser();
    target.InjectFrom<BindAwareInjection>(model);
    return View(target);
}

Using that technique, the end user can update the User object's Name property without being able to overwrite the IsSuperUser property.

NOTE

It's true that MVC's UpdateModel() method obeys BindAttribute inclusion/exclusions found on the target object, so you may ask why we need to use ValueInjecter at all. However, that would necessitate the target object type to contain the Bind attributes. In our case, we want the source and target to be of different types, so that the Source (ViewModel) type has the Bind exclusions, leaving the Target type (Source's super-type) undecorated. This approach allows us to declare thin ViewModels based on underlying domain classes without having to slavishly modify our VM definitions every time the superclass definition changes.

Incidentally, by default the BindAwareInjection uses the Bind attributes from the Source, but you can use the Target attributes instead like this:

[HttpPost]
public ActionResult UpdateUser(UserVM model)
{
    var target = Repository.GetCurrentUser();
    var injection = new BindAwareInjection { BindRuleLocation = BindAwareInjection.BindRuleLocationType.Target };
    target.InjectFrom(injection, model);
    return View(target);
}

MVC Bind Attribute

Just a quick tip. If you want to use the Bind attribute to set the binding whitelist/blacklist (include/exclude) settings, you may have seen many examples where they set the attribute in the Controller Action signature i.e.
 
public ActionResult UpdateThing([Bind(Exclude="IsSuperUser")]UserEntity model)
{
   ...
}

However, I think the following approach is nicer. First define your ViewModel, which simply extends your entity model. Note that we define the Bind Attribute on the class definition itself.

[Bind(Exclude="IsSuperUser")]
public class VM_User : UserEntity {}

Then you just bind from the ViewModel in your Action.

public ActionResult UpdateThing(VM_User model)
{
   ...
}
To copy property data back from your ViewModel to your entity model, I favour ValueInjecter. I wrote a special Injection class for this purpose: http://zootfroot.blogspot.co.uk/2012/06/custom-valueinjecter-injection-to-use.html

12 Jun 2012

Disappointed with Raspberry Pi

Thought for the day - what's so great about the Raspberry Pi? We've had one in the office for a week and we're already bored of it.

It's not the Pi's fault, either. For me, the problem is one of ridiculous expectations. The talk has all been of a computer that was going to reinvigorate the teaching of computer science and give modern kids an inroad into the subject.

To me, that suggested that the Pi was going to be simple, as in 'so boneheaded that even I can understand how it works'. But the Pi is far from simple. It's a very cleverly miniaturised PC on a single board. Everything on the board is tiny. I really don't think the fact that the guts are exposed will help kids learn what they do, why and how.

Then when you turn it on, it's a regular PC running Linux, only running it a bit slow because it's not very powerful. Again, I don't see how this helps kids out. Linux is a powerful and complicated operating system. A kid can't learn why we need an OS, or how it works under the hood, by just booting Linux.

I guess the thing that blew peoples minds about the Pi is the price. For 20 quid, it is excellent value, for a slightly dozy linux box. But I don't believe the problem that needs solving is one of expense.

Rather, what I was REALLY hoping for was something with the instant feedback that I used to enjoy in the 1980s with my 8-bit machines. I learned to program at an early age because when you turned the computer on (Apple II and ZX Spectrum 48k in my case) it would just sit there with a blinking cursor. You typed your basic program right in on the command line, and ran it.

10 PRINT "HELLO "
20 GOTO 10
RUN

To achieve the same effect on a modern machine requires booting up, logging in, loading a development IDE, building and running. Layers of abstraction and complication. The modern OS multitasks, runs your little program in a GUI window. In my opinion, it's too much. The good old days of the black screen and the flashing cursor put you right in control. You could build up your understanding slowly.

Regarding the hardware, I would have loved to have understood exactly how my computer worked at a deep level even back in the 80s. I don't know where kids are going to get that low-level experience now. Playing with Arduino-type hardware might be a way, but it still needs programming. I seem to remember playing with physical logic gate boards at school, which I enjoyed - physical programming. I think teaching electronics at school would be fantastic - capacitors, diodes, circuit boards.

Getting back to the roots has to be the way to learn!

Sending email with inline images in ASP.NET MVC

To use inline images in your email, your mail should use HTML format and refer to the images using the collection of Linked Resources, like so.

using (var client = new SmtpClient())
{
    MailMessage newMail = new MailMessage();
    newMail.To.Add(new MailAddress("you@your.address"));
    newMail.Subject = "Test Subject";
    newMail.IsBodyHtml = true;

    var inlineLogo = new LinkedResource(Server.MapPath("~/Path/To/YourImage.png"));
    inlineLogo.ContentId = Guid.NewGuid().ToString();

    string body = string.Format(@"
            <p>Lorum Ipsum Blah Blah</p>
            <img src=""cid:{0}"" />
            <p>Lorum Ipsum Blah Blah</p>
        ", inlineLogo.ContentId);

    var view = AlternateView.CreateAlternateViewFromString(body, null, "text/html");
    view.LinkedResources.Add(inlineLogo);
    newMail.AlternateViews.Add(view);

    client.Send(newMail);
}

The benefit of this approach over the use of the Attachments collection is that the linked images do not appear in the mail client's list of attachments for the mail.

6 Jun 2012

Override the ASP.NET MVC 3 Nullable Boolean EditorFor EditorTemplate

If you use EditorFor() in your MVC View code and pass it a nullable boolean (bool?), you'll see that MVC helpfully renders a tri-state dropdown list. However, the three choices in the dropdown are "Not Set", "True", and "False" which seem a bit unfriendly to some users.

Save the following template as Boolean.cshtml under Views / Shared / EditorTemplates in your project and you'll see a more friendly widget by default.

@model bool?
@using System.Web.Mvc

@{
    var selectList = new List<SelectListItem>();
    selectList.Add(new SelectListItem { Text = "", Value = "" });
    selectList.Add(new SelectListItem { Text = "Yes", Value = "true", Selected = Model.HasValue && Model.Value });
    selectList.Add(new SelectListItem { Text = "No", Value = "false", Selected = Model.HasValue && !Model.Value });
}

@Html.DropDownListFor(model => model, selectList)


Now you can customise it as you please.

If you don't want to override the standard template, bear in mind that you can save it with a different filename e.g. FriendlyBool.cshtml and then call it explicitly like so:

@Html.EditorFor(model => model.myNullableBool, "FriendlyBool")
If I helped you out today, you can buy me a beer below. Cheers!