ITypeConvention.CanHandle(Type)

9 views
Skip to first unread message

ch...@marisic.com

unread,
Jan 14, 2009, 3:09:30 PM1/14/09
to Fluent NHibernate
I think ITypeConvention.CanHandle should take the IProperty interface
instead of just a type.

I have a ITypeConvention implementation

public class TypePrependsFieldName<T> : ITypeConvention
{
private readonly string _fieldName;

public TypePrependsFieldName(string fieldName)
{
_fieldName = fieldName;
}

public bool CanHandle(Type type)
{
return type == typeof (T);
}

public void AlterMap(IProperty propertyMapping)
{
if (propertyMapping.Property.Name != _fieldName) return;

propertyMapping.TheColumnNameIs
(propertyMapping.ParentType.Name + propertyMapping.Property.Name);
}
}

So that I can add the following conventions

convention.AddTypeConvention(new TypePrependsFieldName<string>
("Name"));
convention.AddTypeConvention(new TypePrependsFieldName<string>
("Description"));

However since

Conventions.cs
public ITypeConvention FindConvention(Type propertyType)
{
var find = _typeConventions.Find(c => c.CanHandle
(propertyType));
return find ?? new DefaultConvention();
}

uses the Type, it only finds the first TypePrependsFieldName class
because it thinks it can handle the property, but it can't because it
doesn't know until AlterMap gets called to check the name of the
property.

Or at the very least to change it from Type to be PropertyInfo since
you can always go up to the type anyway since that's exactly what the
code does that calls FindConvention does anyway

AutoMapColumn.cs
private bool HasExplicitTypeConvention(PropertyInfo property)
{
var convention = conventions.FindConvention
(property.PropertyType);
....
}

I know this would be a breaking change but it's very hard to figure
out if the Convention can handle the type unless you end up creating a
very complex AlterMap method and have to merge a bunch of classes
together as opposed to being able to add ones like I did using a
little bit of generics.

James Gregory

unread,
Jan 14, 2009, 3:33:57 PM1/14/09
to fluent-n...@googlegroups.com
Well, ITypeConvention kind of implies a type, don't you think? :)

There's already an IPropertyConvention, which you can probably make use of; there's an AttributeConvention<T> you could probably use for reference. I've yet to write anything up on it yet though.

ch...@marisic.com

unread,
Jan 14, 2009, 4:50:09 PM1/14/09
to Fluent NHibernate
Great my post got eaten by google...

Looking up IPropertyConvention, this behaves quite differnetly than
ITypeConvention CanHandle/AlterMap vs a single Process method, the
only usage is for the AttributeConvention class. the
List<IPropertyConvention> collection is private and can only be
accessed by the ForAttribute<T>(Action<T, IProperty> action) method.

Is there a reason that ITypeConvention and IPropertyConvention are so
different in these aspects?

James Gregory

unread,
Jan 14, 2009, 5:20:11 PM1/14/09
to fluent-n...@googlegroups.com
Brought this back to the list...


I'm not too concerned with the current convention interfaces. I need to put some more thought into it, but I see the main problems as: ITypeConvention only operates against property's, which means it's very similar to IPropertyConvention; however, ITypeConvention should really work against ANY mapping that has the particular type. If that was the case, then the similarity between the two is certainly a lot less. Then there'd be variations on IPropertyConvention for each particular mapping. I see it being something like this:

interface ITypeConvention
{
  bool CanHandle(Type type);
  void AlterMapping(IMapping mapping);
}

interface IPropertyConvention { ... }
interface IIdConvention { ... }
interface IHasManyConvention { ... }

This does lead to a less genericised model, which will mean more classes and work on our side, but it should give more power to the user.

James Gregory

unread,
Jan 14, 2009, 5:20:45 PM1/14/09
to fluent-n...@googlegroups.com
I'm going to expose the IPropertyConvention, then set at creating some more specialised conventions.

James Gregory

unread,
Jan 14, 2009, 6:25:47 PM1/14/09
to fluent-n...@googlegroups.com
Committed the fix to expose an AddPropertyConvention on the Conventions class. Working on the other conventions now.

ch...@marisic.com

unread,
Jan 15, 2009, 12:42:04 PM1/15/09
to Fluent NHibernate
With these changes checked in, this is what I was able to do with it.
I figure this will give people some ideas

public class TypePrependsPropertyName<T> : IPropertyConvention
{
private readonly string _propertyName;

public TypePrependsPropertyName(Expression<Func<T, object>>
expression)
{
_propertyName = ReflectionHelper.GetProperty(expression).Name;
}

#region Implementation of IPropertyConvention

public bool CanHandle(IProperty property)
{
return property.Property.Name == _propertyName;
}

public void Process(IProperty propertyMapping)
{
propertyMapping.TheColumnNameIs
(propertyMapping.ParentType.Name + propertyMapping.Property.Name);
}

#endregion
}

convention.AddPropertyConvention(new TypePrependsPropertyName<Category>
(c => c.Name));
convention.AddPropertyConvention(new
TypePrependsPropertyName<Territory>(t=>t.Description));

So now all my entities with a .Name property will map to
ObjectTypeName and all with.Description will map to
ObjectTypeDescription which is how the Northwind DBO has their naming
scheme.

ch...@marisic.com

unread,
Jan 15, 2009, 2:23:48 PM1/15/09
to Fluent NHibernate
Something else I came up with this, mapping db column RegionID to a
Region Enum

public class ForeignKeyMapsToEnum<T> : IPropertyConvention
{
private readonly PropertyInfo _property;

public ForeignKeyMapsToEnum(Expression<Func<T, object>>
expression)
{
_property = ReflectionHelper.GetProperty(expression);

if (!_property.PropertyType.IsEnum)
throw new ArgumentException(string.Format("Type: {0}
is not an enum", typeof (T).Name));
}

#region Implementation of IPropertyConvention

public bool CanHandle(IProperty property)
{
return _property.PropertyType ==
property.Property.PropertyType;
}

public void Process(IProperty propertyMapping)
{
propertyMapping.TheColumnNameIs
(propertyMapping.PropertyType.Name + "ID").CustomTypeIs
(propertyMapping.PropertyType);
}

#endregion
}

Usage with convention

convention.AddPropertyConvention(new ForeignKeyMapsToEnum<Territory>
(t=>t.Region));

This one might be a little useful to others but I think between these
2 examples that it's very clear how powerful this feature is.
Reply all
Reply to author
Forward
0 new messages