PrimeDigit – A Design Blog by Will Shaver

December 24, 2008

GAC Conflicts with ASP MVC

Filed under: ASP.Net,c# — Will @ 10:04 am

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.

December 18, 2008

Round datetime to nearest 5 minutes

Filed under: Uncategorized — Will @ 12:01 pm

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))

December 15, 2008

Personal Blog

Filed under: Uncategorized — Will @ 2:06 pm

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.

December 10, 2008

Headaches with ASP.MVC UI Helpers

Filed under: Uncategorized — Will @ 1:49 pm

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:
ListItems

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.

December 2, 2008

Sql Server DateTime Conversion

Filed under: NHibernate,c# — Will @ 10:05 am

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…

December 1, 2008

JQuery In Action

Filed under: Uncategorized — Will @ 2:30 am

JQuery In Action:
347 Pages, not a single mention of the queue() method.

To be fair, they do mention “mainaining state”. (sic)

Powered by WordPress