I’ll be spending the next two days at a Agile Open Northwest. If you’re at the conference, come find me. :)
Otherwise I’ll be blogging about anything I find especially insightful.
I’ll be spending the next two days at a Agile Open Northwest. If you’re at the conference, come find me. :)
Otherwise I’ll be blogging about anything I find especially insightful.
I’ve spent most of the past couple of days mapping a legacy database with NHibernate. Here’s how the design of this database probably went down:
Pointy Haired Boss: We’ve decided to cut costs by paying you per primary key instead of per hour.
DBA: Works for me.
Seriously. We have tables here with two varchars, one decimal, and one int ALL mapped to a primary key. So glad that I have NH to help me out here.
Simple enough, it looks like some standards nazi went through and renamed the Login to Logon in several places for RC1. Unfortunately it did not get renamed in the Web.config for the redirect on failed login url.
<authentication mode="Forms">
<forms loginUrl="~/Account/Login"/>
</authentication>
Where it needs to be:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn"/>
</authentication>
But when I went to the asp.net forums to report this bug, I did the conscientious thing and tried to determine if it had been reported already. Searching for “RC1 logon login” yielded too many results. So I’ll wade through the tree view to only check the asp.net mvc forum. (2004 called, wants it back.) I then realized it was sorting by some kind of relavance instead of date. Where’s the date sort? Lets try going back to the “more search options” Ohh, thanks for clearing my existing search. Perhaps if I hit “back” instead? Still no luck. Ohh wait, there’s a big notice at the top of the search page:
Search will be undergoing maintenance to improve results on Tuesday, January 6, 2009 from 7pm – 10pm Eastern Time, United States.
Ohh. So this is the IMPROVED version? Sigh. I’ll post it here instead.
As I’m sure you noticed, MVC RC1 is out now. In reading through the release notes I found this:
“The overloads for DropDownList and ListBox helper methods were reworked to fix various usability problems that were reported by customers.”
Sometimes using a stock photo can help sell your product. Perhaps this one is trying to convince the buyer what not to do. Or something.

By some strange combination of beta and pre-release candidate installs I managed to create some serious conflicts with ASP.NET MVC. Thereby producing this wonderful little error:
Compiler Error Message: CS0433: The type 'System.Web.Mvc.FormMethod' exists in both 'c:\WINDOWS\assembly\GAC_MSIL\System.Web.Mvc\1.0.0.0__31bf3856ad364e35\System.Web.Mvc.dll' and 'c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\06c0b3cf\32bec25d\assembly\dl3\71bfb65b\e11bf5c5_9e55c901\System.Web.Mvc.DLL'
I am using my own build of the source from codeplex so that I can debug through it to improve my knowledge of the codebase. From research online it looks like the assembly version between the latest pre-release and the beta weren’t incremented, or the web-runner tries to look in the GAC before the /bin, or the evil garden gnomes are at it again, or Microsoft wants to punish me for building THEIR dll files.
To solve this problem, i opened up the AssemblyInfo.cs files of the Mvc project and upped the AssemblyVersion to 1.0.0.9. Then hacked my web config to use that version. As I’m running in debug mode, and can’t sign the assembly I also had to remove the PublicKeyToken from the reference.
<add assembly="System.Web.Mvc, Version=1.0.0.9, Culture=neutral"/>
And suddenly we’re back in business.
This took me forever to find online, so I’m posting it here for the next helpless soul. If you need to round datetimes coming out of sql to the nearest 5 minutes, here you go.
dateadd(mi,(datepart(mi,dateadd(mi,1,DateTimeColumn))/5)*5,dateadd(hh,datediff(hh,0,dateadd(mi,1, DateTimeColumn)),0))
If you want to hear random drivel about my life, I’ve started a personal blog over at willshaver.com. I will continue to use this one for software related posts.
I’ve been beating my head against the UI helpers a lot lately and wanted to share a frustration point. Say I want to do something using the DropDownList, so you map a property on your page that returns a Select List:
<%= Html.DropDownList("db", SelectList)%>
public static SelectList SelectList
{
get
{
List<ListItem> items = new List<ListItem>
{
new ListItem {Selected = true, Text = "All", Value = ""},
new ListItem {Text = "Apples", Value = "Ap"},
new ListItem {Text = "Bananas", Value = "Bn"},
new ListItem {Text = "Oranges", Value = "Or"}
};
return new SelectList(items);
}
}
Seems simple enough right? I’m giving the select list a list of ListItem. I’m using YOUR api. I don’t expect that THIS is what I get back:

Looks like you’re assuming that I won’t use your list of items:
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "Operation performs conversions and returns a unique instance on each call.")]
public virtual IList<ListItem> GetListItems() {
return (!String.IsNullOrEmpty(DataValueField)) ?
GetListItemsWithValueField() :
GetListItemsWithoutValueField();
}
Perhaps a check to see if the items in the enumerable ARE already LISTITEM would be appropriate?
For the record, changing it to this makes the code work:
return new SelectList(items, "Value", "Text", items[0]);
But what happened to convention over configuration?
Edit: ok, the “correct” way I posted wasn’t actually correct. You can’t put the “Selected” item in the selectedValue field, you have to put the value there, in this case “Bn” or “Ap” etc. Heaven forbit putting the actual selected item as the parameter. Or pulling the selected item automatically from request values.
Suppose that your model requires selecting over a date range. It is frequently more convenient to store DateTime.MinValue than it is to store a NULL as this allows for easy selecting without doing a bunch of field < value or null or field > value or not null monkey business. The only problem is that DateTime.MinValue doesn’t fit into SQL server, and DateTime.MaxValue won’t store correctly either. You can even end up where you get a situation where you are storing DateTime.MaxValue then checking a freshly loaded entity to see if it equals DateTime.MaxValue and it doesn’t!
One solution is to create an extension method to trim down the DateTime to only include valid values in a deterministic way as follows:
public static class DateTimeExtensions
{
private static readonly DateTime minSqlDateTime = DateTime.Parse("1/1/1753 12:00:00 AM");
public static DateTime ToSqlDateTime(this DateTime dt)
{
if (dt < minSqlDateTime)
dt = minSqlDateTime;
return new DateTime(dt.Ticks - (dt.Ticks % 10000000));
}
}
Note that this is designed for SQL Server. Other database types may need different levels of precision.
Instead you could implement a custom IUserType for NHibernate which tells NHibernate how to translate a DateTime that is out of bounds into one that is in bounds such as:
public class SqlDateTimeUserType : IEnhancedUserType
{
private static readonly DateTime minSqlDateTime = DateTime.Parse("1/1/1753 12:00:00 AM");
public new bool Equals(object x, object y)
{
return x == null ? y == null : x.Equals(y);
}
public Type ReturnedType
{
get { return typeof(DateTime); }
}
public SqlType[] SqlTypes
{
get { return new[] { NHibernateUtil.DateTime.SqlType }; }
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader dr, string[] names, object owner)
{
//no need to alter values coming out of the database
DateTime? obj = NHibernateUtil.DateTime.NullSafeGet(dr, names[0]) as DateTime?;
return obj == null ? ToSqlDateTime(DateTime.MinValue) : obj.Value;
}
public void NullSafeSet(IDbCommand cmd, object obj, int index)
{
//set it to a safe value going into the database
((IDataParameter)cmd.Parameters[index]).Value = ToSqlDateTime((DateTime)obj);
}
public object DeepCopy(object value)
{
return value;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return cached;
}
public object Disassemble(object value)
{
return value;
}
public bool IsMutable
{
get { return false; }
}
public object FromXMLString(string xml)
{
return DateTime.Parse(xml);
}
public string ObjectToSQLString(object value)
{
return value as string;
}
public string ToXMLString(object value)
{
return value as string;
}
private static DateTime ToSqlDateTime(DateTime dt)
{
if (dt < minSqlDateTime)
dt = minSqlDateTime;
return new DateTime(dt.Ticks - (dt.Ticks % 10000000));
}
}
These user types are mapped as follows:
<property name="ImportDate" type="Framework.SqlDateTimeUserType, Framework"/>
The only problem with this is that a simple test such as the following will fail:
[Test]
public void CanSaveBadValuesToImportDate()
{
With.AutoRollbackTransaction(() =>
{
Product p = new Product("Apples");
p.ImportDate = DateTime.MinValue;
Session.Save(p);
Session.Flush();
Session.Clear();
Product p2 = Session.Load<Product>(p.Id);
Assert.AreEqual(p.Name,p2.Name);
Assert.AreEqual(p.ImportDate, p2.ImportDate); //fails here
});
}
We could try and create a bunch of edge conditions on our converter such as:
public object NullSafeGet(IDataReader dr, string[] names, object owner)
{
DateTime? obj = NHibernateUtil.DateTime.NullSafeGet(dr, names[0]) as DateTime?;
if (obj != null && obj == minSqlDateTime)
return DateTime.MinValue;
return obj == null ? ToSqlDateTime(DateTime.MinValue) : obj.Value;
}
But that could get out of hand in a hurry. It also would cause errors if you stored DateTime.MinValue.AddSeconds(1). If we wanted to go really over the top, we could create our own class that implements operator DateTime (explicit and implicit) which would do the conversion to a SQL range when we first assign a value. This would solve both problems, but you’d end up with your domain needing a special DateTime class for all of your entities instead of using the built in. Your call…
Powered by WordPress