.WithTable in IAutoMappingOverride doesn't override IClassConvention

92 views
Skip to first unread message

Billy

unread,
Mar 24, 2009, 4:07:32 PM3/24/09
to Fluent NHibernate
Hi James,

I'm updating S#arp Architecture's example project to the last and have
made great progress. In fact, the thing remaining is a problem I'm
having with an override not "taking."

I have the following class convention:

public class TableNameConvention : IClassConvention
{
public bool Accept(IClassMap classMap) {
return true;
}

public void Apply(IClassMap classMap) {
classMap.WithTable(Inflector.Net.Inflector.Pluralize
(classMap.EntityType.Name));
}
}

I then have an override for a Region class as follows:

public class RegionMap : IAutoMappingOverride<Region>
{
public void Override(AutoMap<Region> mapping) {
mapping.WithTable("Region");
...
}
}

The .WithTable in the override is not getting applied, although the
other settings within the override are being applied just fine. When
I do a select for any region objects, the SQL is trying to query a
table named Regions (which is compliant with the convention, but not
with the override).

Ideas?

Thanks!
Billy McCafferty

Billy

unread,
Mar 24, 2009, 4:13:20 PM3/24/09
to Fluent NHibernate
Looks like this is related to the issue described at
http://groups.google.com/group/fluent-nhibernate/browse_thread/thread/b866ce932b2a4f89

Incidentally, if I modify the class convention to ignore the Region
class, then the override works just fine; e.g.,

public class TableNameConvention : IClassConvention
{
public bool Accept(IClassMap classMap) {
return classMap.EntityType != typeof(Region);
}

public void Apply(IClassMap classMap) {
classMap.WithTable(Inflector.Net.Inflector.Pluralize
(classMap.EntityType.Name));
}
}

But preferably, I don't want to have to put class exclusions within
the convention classes, I'd rather have the overrides always take
precedence.

Billy

James Gregory

unread,
Mar 24, 2009, 4:29:37 PM3/24/09
to fluent-n...@googlegroups.com
Yep, something smelly is going on here. I'll investigate.

James Gregory

unread,
Mar 25, 2009, 5:56:20 AM3/25/09
to fluent-n...@googlegroups.com
Just looked into this, it seems overrides are being applied before conventions. So the changes get overwritten by the conventions... The thread you linked to is a bit of a bigger issue, but for this one I should just be able to reorder when the overrides get applied.

An alternative is to not use overrides at all for this situation, as you're not actually doing anything with the automappings. You could just write another convention that's RegionTableNameConvention.

Still, I'll fix this either way.

James Gregory

unread,
Mar 25, 2009, 6:02:01 AM3/25/09
to fluent-n...@googlegroups.com
Ah, damn. I can't re-order them. The conventions can't be applied before the overrides because the overrides may alter the underlying classmap, which could result in some conventions not being applied when they should be.

I'd go with my previous suggestion of doing this exclusively with conventions. You've got a few options: you could have your TableNameConvention and a RegionTableNameConvention, or a PluralizedTableNameConvention and a SingularTableNameConvention, or perhaps a Plural attribute that you decorate specific entities with.

Let me know what you think of this.

Martin Hornagold

unread,
Mar 25, 2009, 6:41:39 AM3/25/09
to fluent-n...@googlegroups.com

Billy,

 

I agree with James.

The beauty of the new conventions system is that you can selectively apply conventions to classes.

I notice from your comment about class exclusions and your comments in the PrimaryKeyConvention class in S#arp that you seem to be shying away from this.

 

I have been battling with how to apply conventions/overrides within my project and am now leaning towards James’ approach of targeted conventions with prescriptive class names .

 

So in S#arp’s Northwind sample we should have:

 

IdentityIdConvention

AssignedStringIdConvention

 

Rather than just PrimaryKeyConvention

 

We should have Pluralized and Singular table name conventions.

 

There should also Lazy and NonLazy class conventions which would again reduce the need for overrides and are more maintainable.

 

Just my 2 pennies

 

Martin

Billy

unread,
Mar 25, 2009, 9:38:30 AM3/25/09
to Fluent NHibernate
But to me, an override should do just that: "override." Accordingly,
you can have a "convention" which may get "overridden" on a case by
case basis. Let's assume that I have one object which doesn't adhere
to many conventions. (Granted, this is a bad design, but it certainly
comes up with legacy databases.) Accordingly, I'll have to put an
explicit class and/or property exclusion into a number of convention
classes. To me, that smells of shotgun surgery and doesn't allow one
to nicely encapsulate the overrides in one location. E.e., if the
mapping rules for a customer object changes, which aren't in
compliance with the conventions, then I would expect that I should
only have to look at the customer overrides to make a modification to
the mapping rules. Otherwise, I'm going to have to do a project-wide
find of all the customer exclusions to find the right one to modify.

Billy


On Mar 25, 4:41 am, "Martin Hornagold"
> On Wed, Mar 25, 2009 at 9:56 AM, James Gregory <jagregory....@gmail.com>
> wrote:
>
> Just looked into this, it seems overrides are being applied before
> conventions. So the changes get overwritten by the conventions... The
> thread you linked to is a bit of a bigger issue, but for this one I
> should just be able to reorder when the overrides get applied.
>
> An alternative is to not use overrides at all for this situation, as
> you're not actually doing anything with the automappings. You could just
> write another convention that's RegionTableNameConvention.
>
> Still, I'll fix this either way.
>
> On Tue, Mar 24, 2009 at 8:29 PM, James Gregory <jagregory....@gmail.com>
> wrote:
>
> Yep, something smelly is going on here. I'll investigate.
>

James Gregory

unread,
Mar 25, 2009, 10:07:23 AM3/25/09
to fluent-n...@googlegroups.com
The difference is, you are overriding conventions but you're using an automapping override, which is for overriding how a class is automapped - not the same thing. It's a side-effect of how automapping is designed that you can use an automapping override as the same thing as a conventional override, but it's certainly not designed with that in mind; one day when I've got my benevolent dictator hat on I may fix that issue.

Why do you have to think in terms of a specific entity? It's been a long time since I looked at Northwind, but surely there are a collection of conventions rather than one convention with a single override. You shouldn't think of it as changing a single entity, but all the entities that match that style.

The conventions are designed using interfaces, so if you really do want just one class to be altered, then create a MyEntityOverrideConvention class that implements all the interfaces it needs to change.

public class CustomerOverrides : IClassConvention, IIdConvention
{
  public bool Accept(IClassMap target)
  {
    return Accept(target.EntityType);
  }

  public bool Accept(IIdentityPart target)
  {
    return Accept(target.EntityType);
  }

  private bool Accept(Type type)
  {
    return type == typeof(Customer);
  }

  public void Alter(IClassMap target)
  {
    target.WithTable("FunnyCustomerTableName");
  }

  public void Alter(IIdentityPart target)
  {
    target.ColumnName("IdSpecialName");

Billy

unread,
Mar 25, 2009, 10:22:24 AM3/25/09
to Fluent NHibernate
That makes sense James...thank you very much for clarifying!

Billy

Martin Hornagold

unread,
Mar 25, 2009, 12:26:51 PM3/25/09
to fluent-n...@googlegroups.com

James/Billy,

 

Attached is a patch with my take on the conventions in Northwind example in S#arp Architecture (patched to revision 420).

 

James,

 

I would be particularly interested on your views and how you would approach it.

There are a couple of overrides still remaining.

The EmployeeTerritories many to many is a stumbling block. I feel that this should be a convention but not really sure how to achieve it.

See my comment in the ManyToManyTableNameConvention class for details:

 

public class ManyToManyTableNameConvention : IHasManyToManyConvention

    {

        public bool Accept(IManyToManyPart target)

        {

            return true;

        }

 

        public void Apply(IManyToManyPart target)

        {

            // want to be able to do something like the following to

            // account for the EmployeeTerritories case:

 

            // var tableName = target.IsInverse

            //                    ?

            //                        target.ChildType.Name + Inflector.Net.Inflector.Pluralize(target.EntityType.Name)

            //                    :

            //                        target.EntityType.Name + Inflector.Net.Inflector.Pluralize(target.ChildType.Name);

            // target.WithTableName(tableName);

        }

    }

 

Cheers,

SharpNorthwindConventions.patch

Martin Hornagold

unread,
Mar 25, 2009, 12:41:51 PM3/25/09
to fluent-n...@googlegroups.com

James,

 

One other thing that deserves mentioning is the IdentityIdConvention.

It accepts anything that isn’t accepted by the AssignedIdentityConvention like so:

 

public class IdentityIdConvention : IIdConvention

    {

        public bool Accept(IIdentityPart target)

        {

            return target.IdentityType == typeof (int) &!

                (new AssignedIdConvention()).Accept(target);

        }

 

        public void Apply(IIdentityPart target)

        {

            target.GeneratedBy.Identity();

        }

    }

 

Assuming you don’t think this use case is garbage it would be useful to have a static method available for conventions to check whether a target would be accepted.

The above code could then be modified as follows:

 

public class IdentityIdConvention : IIdConvention

    {

        public bool Accept(IIdentityPart target)

        {

            return target.IdentityType == typeof (int) &!

                AssignedIdConvention.Accepts(target);

        }

 

        public void Apply(IIdentityPart target)

        {

            target.GeneratedBy.Identity();

        }

    }

 

 

Thoughts?

Reply all
Reply to author
Forward
0 new messages