RavenDB – Input string was not in a correct format

Leave a comment

In a RavenDB ASP.NET MVC app I was recently working on, I was constantly seeing good ol’ System.FormatException – “Input string was not in a correct format”.

My entities all use ints for their Ids.

After a while I noticed (via the RavenDB Management Console) that my entities had Ids like “tab/123″, but previously the Ids were like “tab-123″.

The problem turned out to be that I had accidentally removed this line while doing some refactoring:


documentStore.Conventions.IdentityPartsSeparator = "-";

Once I put that back in I stopped seeing the System.FormatException and all was right in the world.

 

Retrieve child items with an integer ID in a many-to-many with RavenDB

Leave a comment

I have a many to many relationship, like so

public class Movie
{
   public int Id { get; set; }
   public string Title { get; set; }
   public int[] ActorIds { get; set; }
   // etc
}

public class Actor
{
   public int Id { get; set; }
   public int Name { get; set; }
   // etc
}

When I load a Movie, I also want to load all of the Actors associated with that Movie.

This can be done using Raven’s Load<T>(IEnumerable<System.ValueType> ids) method, and casting the int[] to an array of ValueType, like so:

var movie = RavenSession.Load<Movie>(movieId);
var actorIds = movie.TabIds.Cast<System.ValueType>();

var actors = RavenSession.Load<Actor>(actorIds).OrderBy(x => x.Name);

Upgrade a Macbook with Boot Camp from Windows 7 to Windows 8 Pro

7 Comments

I have a mid-2011 Macbook Air running both OSX Lion and Windows 7 using Boot Camp.

Over the weekend I upgraded it to Windows 8 by doing the download only installation. The process was quite simple and it worked fine. Here’s the steps:

  1. Boot into Windows 7
  2. Go to http://windows.microsoft.com/en-GB/windows/download-shop and click “Buy Windows 8″. Download the installer and start it up.
  3. Pay! The installer will ask for you billing details, credit card / debit card / Paypal details etc. At £25 I thought it was a cheap enough upgrade, especially since I would be going from Windows 7 Home Premium to Windows 8 Pro which means I’d get Remote Desktop etc.
  4. Wait half an hour while the installer downloads the OS.
  5. Once the install starts, it will ask you if you want to
    1. Keep all your programs and your personal info and settings
    2. Keep your personal settings only
    3. Keep nothing. I went for this option as I always like to start fresh.
  6. Now I had a few problems. The install demanded that I have 20Gb free on my C: and I only had about 5 Gb free. I had to stop the installation to free up some disk space. I did this by:
    1. Running Disk Cleanup
    2. Disabled Virtual memory, which shrunk the pagefile.sys from 4Gb to 0Gb.
    3. Disabled hibernate, which removed the 4Gb hiberfile.sys
    4. Deleted c:\Windows\TrustedInstaller folder – but that was a mistake, because then I couldn’t uninstall any programs to free up the necessary space!
    5. Since I was choosing to keep nothing on C: anyway I deleted folders willy-nilly from C:\Program Files and C:\Program Files (x86)
  7. After that I was able to continue the installation which took another 20 mins or so. It rebooted 3 or 4 times, during which  I had to hold down the Mac’s option key at boot and choose the Windows partition.
  8. All done! Windows 8 works fine.

Once it was installed I did a couple of clean-up tasks.

  1. My C: was almost full again, because the install had backed up everything in the C:\Windows.old folder. So I ran Disk Cleanup again and clicked the “Clean up system files” button to remove those.
  2. I no longer had Apple’s Boot Camp drivers installed in Windows, so I had boot into OSX, start the Boot Camp helper and copied the Boot Camp drivers to a USB memory stick. Then rebooted into Windows and installed those drivers. Worth doing so that the Mac’s hotkeys (volume up/down, screen brightness etc) would work.

Regular expression replace in Visual Studio

1 Comment

I can never remember how to do replaces in Visual Studio using regular expressions, so here’s a quick example.

I’ve got 30 classes that implement BasePage. I want to change this:

public class VariationsPage : BasePage

to this:

public class VariationsPage : BasePage<VariationsPage>

The regex should find:

public class {.*} \: BasePage

and replace with:

public class \1 \: BasePage<\1>

Meteor todos sample – add buttons to move items up and down

Leave a comment

This is the sixth in a x part series about Meteor for beginners.

Let’s add the buttons to the UI first. I’ve decided to put them next to the delete button, so I will copy the html and css for the “destroy” class so that the UI is consistant.

<template name="todo_item">
  <li class="todo {{done_class}}">
    {{#if editing}}
      <div class="edit">
        <input id="todo-input" type="text" value="{{text}}" />
      </div>
    {{else}}
      <div class="move-up"></div>
      <div class="move-down"></div>
      <div class="destroy"></div>
      ...
#item-list .todo .destroy {
    cursor: pointer;
    position: absolute;
    margin-left: 50px;
    left: 5px;
    top: 15px;
    height: 20px;
    width: 20px;
}

#item-list .todo .move-up {
    cursor: pointer;
    position: absolute;
    left: 5px;
    top: 15px;
    height: 20px;
    width: 20px;
}

#item-list .todo .move-down {
    cursor: pointer;
    position: absolute;
    margin-left: 25px;
    left: 5px;
    top: 15px;
    height: 20px;
    width: 20px;
}

#item-list .todo .display, #item-list .todo .edit {
    margin-left: 80px;
    height: 100%;
    width: auto;
    float: left;
    padding-top: 18px;
    line-height: 1;
}

#item-list .todo:hover .move-up {
    background: url('/arrowup.png') no-repeat 0 0;
}

#item-list .todo .move-up:hover {
    background-position: 0 -20px;
}

#item-list .todo:hover .move-down {
    background: url('/arrowdown.png') no-repeat 0 0;
}

#item-list .todo .move-down:hover {
    background-position: 0 -20px;
}

PS. I spent longer making the up down arrow icons in an image editor than writing the code for this post!

Items in each todo list are currently sorted by their timestamp:

Template.todos.todos = function () {

  ...

  return Todos.find(sel, {sort: {timestamp: 1}});
};

I will continue to use the timestamp to control the sort order. When the up button is clicked, set the current item’s timestamp to be 1ms less than the item above it.

Template.todo_item.events = {
  ...

  'click .move-up': function() {
    var todos = Template.todos.todos().fetch();
    var currentItemIndex = todoRanking(this, todos);

    if (currentItemIndex > 0) {
      var todoAboveMe = todos[currentItemIndex - 1];
      Todos.update(this._id, {$set: {timestamp: todoAboveMe.timestamp - 1}});
    }
  }
var todoRanking = function(todoItem, todoArray) {
  var ids = todoArray.map(function (todo) { return todo._id; });
  return ids.indexOf(todoItem._id);
};

We can do something similar to move an item down.

  'click .move-down': function() {
    var todos = Template.todos.todos().fetch();
    var currentItemIndex = todoRanking(this, todos);

    if (currentItemIndex < todos.length -1) {
      var todoBelowMe = todos[currentItemIndex + 1];
      Todos.update(this._id, {$set: {timestamp: todoBelowMe.timestamp + 1}});
    }
  }

See the code on github.

Meteor todos sample – remove all items

Leave a comment

This is the fifth in a x part series about Meteor for beginners.

Exercise 2: Add a Remove all items button

As usual, let’s add the button to the html first. I’m gonna stick it in the top frame next to the list of tags cos I can’t think of anywhere better to put it.

<template name="tag_filter">
  <div id="tag-filter" class="tag-list">
    <div class="label">Show:</div>
    {{#each tags}}
      <div class="tag {{selected}}">
        {{tag_text}} <span class="count">({{count}})</span>
      </div>
    {{/each}}
    <div class="label">
        <input type="button" class="clear-items" value="Clear all items" />
    </div>
  </div>
</template>

And now the code to handle the button click event:


Template.tag_filter.events = {
  'mousedown .tag': function () {
    if (Session.equals('tag_filter', this.tag))
      Session.set('tag_filter', null);
    else
      Session.set('tag_filter', this.tag);
  },
  'click .clear-items': function() {
    if (confirm('Are you sure you want to remove all todo items from the current list? This action cannot be undone.')) {
      var list_id = Session.get('list_id');
      if (!list_id)
        return;

      Todos.remove({list_id: list_id});
    }
  }
};

Easy!

See the code on github.

Meteor todos sample – friendly URLs

5 Comments

This is the fourth in a x part series about Meteor for beginners.

Introduction

I’ve been trying to get up to speed with Meteor lately. Since I don’t have any experience with nodejs it’s been a challenge.

I started by looking at the leaderboard sample, and then the todos sample. At the end of the video the author lays down a challenge – to make the URLs friendly. So instead of http://localhost:3000/bbbeec7c-6749-400b-bc45-4ed744e010b7 it should be http://localhost:3000/Languages. In order to do that I needed to understand how routing works.

When you click on a todo list in the left, this code fires:

Template.lists.events = {
  'mousedown .list': function (evt) { // select list
    Router.setList(this._id);
  },

which calls:

var TodosRouter = Backbone.Router.extend({
  routes: {
    ":list_id": "main"
  },
  main: function (list_id) {
    Session.set("list_id", list_id);
    Session.set("tag_filter", null);
  },
  setList: function (list_id) {
    this.navigate(list_id, true);
  }
});

The setList function calls navigate, which is Backbone.js’s router function to save the current location in the browser’s history. The “true” parameter also tells the browser to navigate to the url, so if list_id = “1234” the browser will navigate to i.e. localhost:3000/1234.

According to the routes defined here

var TodosRouter = Backbone.Router.extend({
  routes: {
    ":list_id": "main"
  },

if I navigate to localhost:3000/1234, then “1234” will be passed to the “main” function as the “list_id” parameter. Which takes us to:

var TodosRouter = Backbone.Router.extend({
  routes: {
    ":list_id": "main"
  },
  main: function (list_id) {
    Session.set("list_id", list_id);
    Session.set("tag_filter", null);
  },
  setList: function (list_id) {
    this.navigate(list_id, true);
  }
});

OK, so the only thing that the “main” function does is set two Session variables. What effect does that have? Time to look at some html.

client/todos.html:

<template name="todos">
  {{#if any_list_selected}}
  <div id="items-view">
    <div id="new-todo-box">
      <input type="text" id="new-todo" placeholder="New item" />
    </div>
    <ul id="item-list">
      {{#each todos}}
        {{> todo_item}}
      {{/each}}
    </ul>
  </div>
  {{/if}}
</template>

That there is a Handlebars template, with name=”todos”. Interestingly, it has a “#each todos”. The data for that is retrieved by a helper function called “todos” (on the Template also called “todos”), i.e. this:

Template.todos.todos = function () {
  // Determine which todos to display in main pane,
  // selected based on list_id and tag_filter.

  var list_id = Session.get('list_id');
  if (!list_id)
    return {};

  var sel = {list_id: list_id};
  var tag_filter = Session.get('tag_filter');
  if (tag_filter)
    sel.tags = tag_filter;

  return Todos.find(sel, {sort: {timestamp: 1}});
};

This template (like all Meteor templates) reactively monitors the Session and detects that the value for Session.get(‘list_id’) has changed, so it runs again and returns the todo items to be rendered. Each todo item is then rendered by another Handlebars template called todo_item , as per the {{> todo_item}} in the html.

In summary then,

  1. Todo list link is clicked in the left pane, calls TodosRouter.setList(list_id).
  2. TodosRouter.setList navigates to ~/list_id
  3. Router.main sets Session[list_id] variable.
  4. The template helper “todos” detects Session[list_id] has changed so it re-runs.
  5. The Handlebars template named “todos” is re-rendered with the result of the “todos” template helper.

So how do we make the URLs friendly?

Now that we understand how routing works in Meteor, we can take the TodosRouter shown above and change:

  1. TodosRouter.setList(list_id) to TodosRouter.setList(list_name)
  2. TodosRouter.setList should navigate to ~/list_name
  3. Router.main can lookup the correct list_id from the list_name and set the Session[list_id] as before.

So

var TodosRouter = Backbone.Router.extend({
  routes: {
    ":list_name": "main"
  },
  main: function (list_name) {
    var list = Lists.findOne({name: list_name});
    if (list)
      Session.set("list_id", list._id);
    Session.set("tag_filter", null);
  },
  setList: function (list_name) {
    this.navigate(list_name, true);
  }
});

We also need to change all the code which calls setList():

Template.lists.events = {
  'mousedown .list': function (evt) { // select list
    Router.setList(this.name);
  },
Meteor.subscribe('lists', function () {
  if (!Session.get('list_id')) {
    var list = Lists.findOne({}, {sort: {name: 1}});
    if (list)
      Router.setList(list.name);
  }
});
Template.lists.events[ okcancel_events('#new-list') ] =
  make_okcancel_handler({
    ok: function (text, evt) {
      var id = Lists.insert({name: text});
      Router.setList(text);

Finally, for cosmetic reasons, we should change the “lists” template so that the html that’s rendered for the todo list links has the correct href attribute – so that when the user hovers over the link they see the new friendly url.

<a class="list-name {{name_class}}" href="/{{name}}">
  {{name}}
</a>


See the source code on github.

Older Entries Newer Entries

Follow

Get every new post delivered to your Inbox.