views:

225

answers:

2

I'm trying to get Fluent NHibernate 1.0 RTM to map a User entity for me so that I have access to UserId and UserName inside my ASP.NET MVC application via NHibernate.

I have:

public class User
{
    public virtual Guid UserId { get; protected set; }
    public virtual string UserName { get; protected set; }
}

It represents the aspnet_Users table with only the relevant columns to be mapped. This is the only entity that is not being automapped. Here is my mapping:

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.UserId);
        Map(x => x.UserName);
        WithTable("aspnet_Users");
    }
}

Everything else is getting automapped with conventions.

Here are my PrimaryKeyConvention and TableNameConvention:

public class PrimaryKeyConvention : IIdConvention
{
    public void Apply(IIdentityInstance instance)
    {
        instance.Column(instance.Property.ReflectedType.Name + "Id");
        instance.UnsavedValue(System.Guid.Empty.ToString());
        instance.GeneratedBy.GuidComb();
    }
}

public class TableNameConvention : IClassConvention
{
    public void Apply(IClassInstance instance)
    {
        instance.Table(Inflector.Net.Inflector.Pluralize(instance.EntityType.Name));
    }
}

The Mapping process fails right after executing the ClassMap code (which comes before all automapping), followed by the TableNameConvention code, followed by the PrimaryKeyConvention code. The failure is in PrimaryKeyConvention because instance.Property is null. I tried to do an if(instance.Property != null) but that terminates the mapping process early with a "the required attribute 'class' is missing" error. I also had an if (instance.EntityType != typeof(User)) in the TableNameConvention, but took out when it was making no difference.

What is going on here? First of all, why is the AutoMapping processes calling the conventions for the ClassMap? Second, why is the PrimaryKenConvention getting passed an instance.Property == null? How can I get this to work so that the mapping process moves on and maps the rest of my entities using AutoMapping + conventions?

Note, I had this all working for months under an earlier version of FNH prior to the refactor for 1.0 RTM.

A: 

I don't use the auto-mapping myself, but I think you need to map User by implementing IAutoMappingOverride. Something like:

public class UserMap : IAutoMappingOverride<User>
{
    public void Override(AutoMapping<User> mapping)
    {
        mapping.Id(x => x.UserId);
        mapping.Map(x => x.UserName);
        mapping.WithTable("aspnet_Users");
    }
}
Jamie Ide
hi Jamie. The overrides are only for objects that will get mapped as entities by the automapper. I use a base object called Entity to encapsulate Id, CreatedDate, etc. and all entities inherit from it. However, my User object does not inherit anything, since it does not adhere to the entity requirements seeing as how the design is dictated by the aspnet_Users table. I use .ignoreBase<Entity> when setting up the AutoMap. Therefore, I can't do an override the way you recommended since User never gets mapped to begin with.
Chris F
A: 

I've figured out the answer to this.

public UserMap()
{
    Id(x => x.UserId);
    Map(x => x.UserName);
    Table("aspnet_Users");
}

public class PrimaryKeyConvention : IIdConvention
{
    public void Apply(IIdentityInstance instance)
    {
        instance.Column(instance.EntityType.Name + "Id");
        instance.UnsavedValue(System.Guid.Empty.ToString());
        instance.GeneratedBy.GuidComb();
    }
}

public class TableNameConvention : IClassConvention
{
    public void Apply(IClassInstance instance)
    {
        instance.Table(Inflector.Net.Inflector.Pluralize(instance.EntityType.Name));
    }
}

Note that the PrimaryKeyConvention sets the Column name using instance.EntityName rather that instance.Property. The latter was null for the UserMap, so it would crash.

This approach is better than using a conditional statement on (null != instance.Property) and keeping the instance.Property.ReflectedType.Name line, but both work. If you choose to go that route, you have to explicitly set the Column names in the UserMap:

public UserMap()
{
    Id(x => x.UserId).Column("UserId")
                     .GeneratedBy.Assigned();
    Map(x => x.UserName).Column("UserName");
    Table("aspnet_Users");
}
Chris F