Matthew Doyle
Matthew Doyle

Reputation: 1065

fluent nhibernate not generating schema correctly on function

I have a funny feeling this is just my understanding of nHibernate and Fluent nHibernate.

I have a small console application which exports a schema based on a bunch of entity classes. Everything works great until I do this:

    public virtual int UserId { get; set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
    public virtual string Email { get; set; }
    public virtual UserType UserType { get; set; }
    public virtual Client Client { get; set; }
    public virtual string UniqueIdentifier { get; set; }

    public virtual IList<UserAttribute> UserAttributes { get; set; }

    /** CODE IN QUESTION **/
    public virtual string GetAttributeValue(string name)
    {
        var value = UserAttributes.FirstOrDefault(ua => ua.AttributeDefinition.AttributeName == name);
        if (value != null)
            return value.Value;
        else
            return string.Empty;
    }

If I comment out the function, everything works great. Even if I just have the function return string.Empty, everything works. However, once I add back the line:

var value = UserAttributes.FirstOrDefault(ua => ua.AttributeDefinition.AttributeName == name);

Everything breaks with this error:

"The entity '<>c__DisplayClass1' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id)."

I am using auto mapping. What is the cause of this error? I understand the error normally, but it looks like it's failing on an anonymous class. Do I just have a bad misunderstanding about how this is all supposed to work together? I am definitely a nHibernate newbie, both in practice and in philosophy. I'd love to be enlightened!

Upvotes: 2

Views: 411

Answers (2)

Chris Smith
Chris Smith

Reputation: 688

Though, @Firo answer above works, it does not seem to work when you have Linq queries within another Linq query. Example:

DataTable Dt = FillData();
EnumerableRowCollection<DataRow> MasterProfileRows = null;
IList<RecordOwner> OwnedRecs = new List<RecordOwner>();
OwnedRecs.Add(new RecordOwner { ForeignRecordKey = 100 });
MasterProfileRows = from x in dt.AsEnumerable()
                    where OwnedRecs.Any(y => y.ForeignRecordKey.ToString() == x.Field<object>("ProfileID").ToString())
                    select x;

To work around this, I added this into my override to check the number of occurrences of "__displayclass" in the FullName of the class and if its greater than 1, we ignore mapping it.

public override bool ShouldMap(Type type)
{
    // there is a bug where the fluent automapper tries to automap anonymous types within a lambda. the !type.IsDefined(typeof(CompilerGeneratedAttribute), false) works around this issue
    // except where there is a nest linq query.
    // see: 
    // https://github.com/jagregory/fluent-nhibernate/issues/146
    // http://stackoverflow.com/questions/11446155/fluent-nhibernate-not-generating-schema-correctly-on-function/11447966#11447966
    // http://stackoverflow.com/questions/14268568/odd-error-building-fluentnh-configuration
    // http://stackoverflow.com/questions/11446155/fluent-nhibernate-not-generating-schema-correctly-on-function/11447966
    // ignore nested linq queries (linq queries within linq queries) because they cause the auto mapper to error out.
    System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("__displayclass", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
    if(reg.Matches(type.FullName).Count > 1)
        return false;

    return !type.IsDefined(typeof(CompilerGeneratedAttribute), false);
}

Upvotes: 0

Firo
Firo

Reputation: 30803

the lambda ua => ua.AttributeDefinition.AttributeName == name generates an inner class to capture the name parameter. The automapper walks over every type and tries to map it and unfortunatly also picks up the compiler generated one.

You could implement an AutomappingConfiguration to exclude the compiler generated classes

class MyAutomappingConfiguration : DefaultAutomappingConfiguration
{
    public override bool ShouldMap(Type type)
    {
        return !type.IsDefined(typeof(CompilerGeneratedAttribute), false) && base.ShouldMap(type);
    }
}

Upvotes: 6

Related Questions