2007/11/09

WTF: Overloading and Generics

Recently I discovered some behaviour of .NET that I couldn't explain. I still wonder whether this is an error or "ByDesign". I found it when I tried to call ActiveRecordMediator's Exists method, which threw out an unexpected exception. Minutes later, I stared unbelievingly to the screen: A complete different method overload was called. I sat down and created a short example that doesn't use ActiveRecord, so it can be understood from any .NET-developer:
public interface IString
{
  string Content{ get;}
}

public class DString:IString
{
  private readonly string content;

  public string Content
  {
    get { return content; }
  }

  public DString(string content)
  {
    this.content = content;
  }
}

public static class Class1
{
  public static void Foo(params IString[] bars)
  {
    foreach (IString s in bars)
    {
      Console.WriteLine(s.Content);
    }
  }

  public static void Foo<T>(T bar)
  {
    throw new NotImplementedException();
  }

}

[TestFixture]
public class Tester
{
  [Test]
  public void CallBar()
  {
    Class1.Foo(new DString("baz"));
  }
}
So what would you expect when running the unit test? That's what I got: TestCase 'ClassLibrary2.Tester.CallBar' failed: System.NotImplementedException : The method or operation is not implemented. What happens here? If you add exactly one optional parameter, the compiler somehow thinks, you meant Foo<istring>() instead of Foo(). This happens only if the type parameter is an interface. I tried with string and that worked like expected, calling Foo(). So, is this a bug or a documented behaviour. Do you know ressources that document it? I didn't find any...

2007/11/06

From The Book of Ideas: Searchable ActiveRecord-Entities

One of the feature requests that I hear most often from my users that they want a search function for the grids. I then show them a search form where they can specify field and values exactly. Most often, I then hear: "No, I meant a search field. I enter some text and the software shows all relevant entries." I usually answer by asking whether it should also save the climate and overcome poverty or if simple mindreading is sufficient. Ok, but last night I had an idea how searching with a single textfield interface can be accomplished without redeveloping that nasty thing for each view. I use the Castle stack for .Net for my applications, which means that my entities are created with Castle ActiveRecord and displayed in MonoRail web applications. The idea works like this: There is only one who knows which properties must be included into the search and this the developer that created the entities. Therefore we need some mechanism to specify the searchable properties. My idea was adding a Searchable attribute to the ActiveRecord classes' properties that need to be included in such searches:
    [ActiveRecord]
    public class Entity : ActiveRecordBase<Entity>
    {
        private int id;
        private string name;
        private string entityDesc;
        private string internalName;

        [PrimaryKey]
        public int Id
        {
            get { return id; }
            set { id = value; }
        }

        [Searchable][Property]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        [Searchable(FriendlyName = "Description")][Property]
        public string EntityDesc
        {
            get { return entityDesc; }
            set { entityDesc = value; }
        }

        [Property]
        public string InternalName
        {
            get { return internalName; }
            set { internalName = value; }
        }
    }
 
The Searchable takes an optional parameter, FriendlyName that allows to specify a localized or other, more user-friendly name. The attribute can be applied to both properties and relations. If applied to a relation attribute (BelongsTo, HasMany, HasAndBelongsToMany etc.) the properties tagged in that type will be added to the search as well. Then there is a SearchMediator, which will create a NHibernate Criteria Query that will check for every search term whether it can be parsed by the datatype of a tagged property and adds it to the query if this is the case. The code below sketches how the query could be created:
public static T[] Search(string text)
{
    string[] words = text.Split();
    DetachedCriteria criteria = DetachedCriteria.For<T>();
    foreach (string word in words)
    {
        Disjunction group = Expression.Disjunction();
        foreach (PropertyInfo info in typeof (T).GetProperties())
        {
            if (info.GetCustomAttributes(typeof (SearchableAttribute), true).Length > 0)
                group.Add(Expression.Like(info.Name, word, MatchMode.Anywhere));
        }
        criteria.Add(group);
    }
    return ActiveRecordMediator<T>.FindAll(criteria);
}
This can be extended by many means. Examples include:
  • Allowing propertyName:searchTerm to narrow search to a specific property by its name or friendly name.
  • Allowing other operators than the colon like price>10.00 or name!Foo
  • Add globbing to specify whether exact matches or all matches should be found
  • Specify the MatchMode behavior for string properties as part of SearchableAttribute

Using that mechanism, I can add searching to almost any entity similar to validation. What do you think, would such a Search component benefit the Castle Framework?

That is only some thoughts. I didn't start implementing anything yet. However, anyone interested in helping is welcome, of course.