PrimeDigit – A Design Blog by Will Shaver

January 28, 2009

MVC RC1

Filed under: ASP.Net,c# — Will @ 11:05 am

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.”

Thanks for listening!

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

September 9, 2008

ModelStateDictionary and ParameterBinding

Filed under: ASP.Net,c# — Will @ 11:14 am

I laid this out in more detail on the forums here:
http://forums.asp.net/p/1317154/2610342.aspx#2610342

I thought I’d mention it to this group so that people were aware of this difference between the interaction the old ParameterBinders and Rescues in version 4 and the new ModelBinders and HandleErrorAttributes of version 5.

The new way of doing parameter binding expects all errors to be placed into the ModelStateDictionary specified in the GetValue method.
public override object GetValue(ControllerContext controllerContext, string modelName, Type modelType, ModelStateDictionary modelState) {

The ModelStateDictionary can then be checked in the controller for errors via ViewData.ModelState.IsValid

I think the idea is that multiple binding errors can then be reported to the user at one time:
foreach (var value in ViewData.ModelState.Values)
{
value.Errors…..
}

Unfortunatly our current NameValueDeserializer that is used by the DeserializeAttribute throws exceptions when it encounters errors. These exceptions are NOT caught by Rescues / HandleErrorAttributes. The ControllerActionInvoker (in preview 5) calls

IDictionary parameters = GetParameterValues(methodInfo);

before the try/catch block that passes off errors to the HandleErrorAttributes. So if you’re using any kind of attribute deserialization and expecting your exceptions to be displayed all pretty like in your rescues… then move line #177 to line #180 of your ControllerActionInvoker.cs :)

-Will

August 26, 2008

ISession Extension for Saving Multiple Objects

Filed under: NHibernate,c# — Will @ 3:36 pm

Incredibly simple, but quite useful:

        public static void Save(this ISession session, params object [] entities)
        {
            foreach (var o in entities)
            {
                session.Save(o);
            }
        }

You can use it like:

session.Save(a,b,c,d,….);

with a list of objects of any type. Useful for unit testing where you want to save multiple items at once.

[Update]

Probably should call it something other than Save, such as SaveAll or SaveList because if you only pass two items to the overload it calls the standard Save(object, int id) version. :(

June 16, 2008

CriteriaByLongAlias

Filed under: NHibernate,c# — Will @ 9:01 am

This simple function cleans up a lot of the Alias sillyness I have fought with in NHibernate.

        public static ICriteria CriteriaByLongAlias(this ICriteria criteria, string field)

        {

            ICriteria byAlias = criteria.GetCriteriaByAlias(criteria.Alias + “_” + field) ??

                                criteria.CreateCriteria(field, criteria.Alias + “_” + field);

            return byAlias;

        }

It is used any time you would previously call CreateCriteria and pass an alias.
Before:
session.CreateCriteria(typeof (Product)).CreateCriteria(“Orders”,”Product_Orders”);
After:
session.CreateCriteria(typeof(Product)).CriteriaByLongAlias(“Orders”);

The alias part is done automatically based on the criteria path. The advantage is that it works recursively, allowing for multiple criteria to be created, and then referenced without duplicates.

ICriteria criteria = session.CreateCriteria(typeof(Product)).CriteriaByLongAlias(“Orders”).CriteriaByLongAlias(“Customer”).Add(Expression.Eq(“Name”, “Ace Hardware”));
criteria.CriteriaByLongAlias(“Orders”).Add(Expression.Eq(“OrderNumber”,12354));

etc.

Note that in order for it to work, you have to create all your subcriteria with it.

-Will

June 4, 2008

Cool Code – Assembly.FindType

Filed under: c# — Will @ 3:06 pm

Here’s an extension to Assembly that I found useful for finding a type. I recommend doing
typeof(TypeInAssembly).Assembly.FindType(“typename”);

    public static class AssemblyExtensions

    {

        public static Type FindType(this Assembly assembly, string typename)

        {

            return assembly.FindTypes(typename).FirstOrDefault();

        }

 

        public static Type[] FindTypes(this Assembly assembly, string typename)

        {

            Type[] types = assembly.GetExportedTypes();

            List<Type> found = new List<Type>();

            foreach(Type type in types)

            {

                if(type.Name == typename)

                {

                    found.Add(type);

                }

            }

            return found.ToArray();

        }

    }

April 22, 2008

Contributing to NHibernate

Filed under: Tutorials,c# — Will @ 9:12 pm

Having successfully submitted a few patches to NHibernate [NH-1280][NH-1260][NH-1259], Ayende Rahien asked me to comment on the difficulty of adding a totally new feature, and provide some hints for others. I am by no means an expert on NHibernate, just a regular user so here goes…

My most recent change set was updating the Criteria Query API to allow for queries involving HAVING clauses. In performing this change I also discovered a bug whereby complicated queries would get parameters out of order. In this post I will cover:

  • The feature added (need and implementation)
  • Determining problem need
  • Hints for getting your patch accepted

The Feature

The Criteria Query API provides some valuable tools for creating dynamic queries without doing crazy string manipulation (yuck). I had a situation where I wanted to validate entities to check for duplicates prior to the transaction being committed. Because of the temporal nature of the project I’m working on, I can’t do it with a table constraint or other simple means and need to check valid dates.

To solve it with HQL would involved about 10 lines of messy reflection and string concats where solving it with the criteria API took about 4. Here’s the code:

ICriteria criteria = session.CreateCriteria(typeof(Person));
criteria.SetProjection(Projections.ProjectionList()
  .Add(Projections.Max("Id"))
  .Add(Projections.GroupProperty("Name")))
  .ProjectionCriteria.Add(Restrictions.Gt(Projections.Count("Name"), 1));

The resulting SQL for this code should be:

"SELECT MAX(Id), Name FROM Person GROUP BY Name HAVING COUNT(Name) > 1"

Imagine my surprise when this relatively simple Criteria Query failed while the same HQL query succeeded. After examining the SQL it produced I determined that there was no HAVING clause being produced.

Fearing that I was doing something wrong I opened up the source code for NHibernate and performed a textual search for “having” as I knew that at some point it must be produced by the SQL generator. I was unable to locate anything in the Criteria API that was even remotely related to HAVING queries.

Having exhausted that possibility I posted to the google nhusers group to make sure that I wasn’t missing something. After some discussion it was obvious that this was indeed a lacking feature and that it would be appreciated by the community.

Quick Review

  1. Confirm that the problem is with the codebase not with your use of it.
    1. Review the code to determine if it is possible to produce the desired result.
    2. Post to the nhusers group to make sure you’re using the API correctly.
  2. Confirm the problem isn’t solved in the JAVA version. (If it is, import that fix.)
  3. Migrate the discussion to the NHibernate developer list for help.

After determining that it was indeed a problem with the codebase I launched into patching it. I wrote test cases to be able to debug into the code in the Criteria interface. I recommend adding the folder for your tests as:
\src\NHibernate.Test\NHSpecificTest\NH1999
Then migrating it to the specific issue number after you’ve made progress enough to report the issue. This prevents nasty collision problems if someone else takes the next number.
\src\NHibernate.Test\NHSpecificTest\NH1280
I’ve also recently taken to adding a description after the folder such as “NH1291AnonExample” in my latest patch. This makes it easier to determine which set of tests has failed for other developers.

The patch itself is rather complicated and out of the scope of this post as it contained changes to almost 50 files. In writing the patch I tried a couple of different methods for adding my features and even had to revert a couple of files from the source. I worked from the output to the input, backing out from the SQL generator to the individual Criteria and Criterion classes.

As I wrote the patch I made sure that I didn’t negatively impact other use cases. If your patch breaks other tests it won’t be accepted even if it implements “teh best featre evar”! For added tests I merged my changes into both my active work project and the Linq to NHibernate project. Doing so revealed cases that I hadn’t considered in my initial tests and allowed me to add in those tests and deliver a more solid patch.

Quick Review

  1. Write a small set of failing tests first. Step-Into these tests while debugging for where to start coding.
  2. Don’t fear changing the source, you can always revert!
  3. Start at the interface points and work toward the center where actual work is performed.
  4. Make sure all unit tests, including those in other projects are passing.

Before submitting any patch I always go through a cleanup of my changed code. This involved going through my patch file by file making sure that I’m only submitting code that pertains to the patch and only changes things that need to be changed. (Using TortoiseSVN this is quite simple, folder menu -> check for modifications)

I try and keep my formatting changes to a minimum. Resist the urge to re-arrange existing code, convert tabs to spaces etc. While NHibernate and many other open source projects could use some cleanup work, that should be performed by dedicated committers. In our case the organization is loosely tied to the Hibernate project and would make it more difficult to port newer features if the NHibernate code is re-arranged.

Submit the patch and leave a clear explanation of the problem it solves and how it solves it. Best of luck and thanks for contributing to the project!

Quick Review

  1. Clean up your code before you commit by reviewing the changes.
  2. Add your patch the JIRA, documenting the change need and usage.

February 20, 2008

MVC UnitTestingFramework to MvcContrib

Filed under: Blog,c# — Will @ 9:47 pm

The Unit Testing Framework I posted a couple of days ago has now been moved into the MvcContrib project, making me an official Developer. Woo Hoo. You can check out the updated documentation.

IParseable Interface

Filed under: c# — Will @ 7:57 pm

[Note: I was reminded by my roommate after posting this that static methods aren't allowed in interfaces....]

Today I was working on a JSON to NHibernate bridge when I came across something quite frustrating when trying to convert strings to other types. In this bridge object I’m persisting an object out of the database with NHibernate, then assigning the new values from the JSON string to that object. (Or list of objects.)

This seemed simple enough. I was thinking that it should be easy enough to write a loop that looks at the javascript keys {“Key”:”Value”} and matches them up with the PropertyInfo of the target object. This is trivial when both are strings, but what if the target type is a float, int, bool, date, enum etc?

Most base types have a Parse method of some kind that looks like Parse(string) or Parse(string, IFormatProvider). Here’s a couple of types that implement some variant of the Parse method.

  • System.Boolean
  • System.Byte
  • System.Char
  • System.Data.SqlTypes.SqlBoolean
  • System.Data.SqlTypes.SqlByte
  • System.Data.SqlTypes.SqlDateTime
  • System.Data.SqlTypes.SqlDecimal
  • System.Data.SqlTypes.SqlDouble
  • System.Data.SqlTypes.SqlGuid
  • System.Data.SqlTypes.SqlInt16
  • System.Data.SqlTypes.SqlInt32
  • System.Data.SqlTypes.SqlInt64
  • System.Data.SqlTypes.SqlMoney
  • System.Data.SqlTypes.SqlSingle
  • System.DateTime
  • System.DateTimeOffset
  • System.Decimal
  • System.Double
  • System.Int16
  • System.Int32
  • System.Int64
  • System.SByte
  • System.Single
  • System.TimeSpan
  • System.UInt16
  • System.UInt32
  • System.UInt64
  • System.Windows.Int32Rect
  • System.Windows.Point
  • System.Windows.Rect
  • System.Windows.Size
  • System.Windows.Vector
  • System.Net.NetworkInformation.PhysicalAddress


This leaves me with only one question?

Where’s my Interface?

I can’t do a test for IParseable on the object, I can’t even search for the parse method by name and parameters because each object implements them differently. I could go with only the string parameter which they all implement, but I’ve learned from Civ4 that culture is important, and besides I wouldn’t want to fail the Turkey test.

Of course there’s a reason for this seemingly lazy design – interfaces can’t be declared as static. (That’s a conversation for a different day.) Ok, so given that they can’t, where do we go from here? Lets pretend that we still wanted to use an Interface for these classes.

interface IParseable { }

So now we’ve got an Empty Interface. Perhaps this would be better as a custom attribute, and if so please explain to me how to accomplish this next part with said custom attribute.

We then assign our interface to the DataType and implement a number of Parse methods

    class DataType : IParseable

    {

        public static DataType Parse(string s)

        {

            //blah blah, use the string to create a new DataType

            return new DataType();

        }

    }

Then in the System.Convert class we’d implement some function like this:

    public static T Parse<T>(string s) where T : IParseable

    {

        MethodInfo mInfo = typeof(T).GetMethod(“Parse”, BindingFlags.Public | BindingFlags.Static);

        if (mInfo != null)

        {

            T obj = (T) mInfo.Invoke(null, new object[] { s });

            return obj;

        }

        return default(T);

    }

Other methods could accept CultureInfo, FormatProviders and other params. Someone have a better way of doing this?

Powered by WordPress