4

"Disabled lazy properies fetching for fully_qualified_type_name beacuse it does not support lazy at the entity level".

This warning was reported by NH Profiler, and as a result, I'm experiencing the dreaded SELECT N + 1 side affect. So if 2200 Subgroup entities are returned, an additional query is being executed to retrieve each InvoicePreference entity (2201 queries total). Something about that relationship seems to be causing the issue.

Here are the entities in question and their respective mappings.

Entity 1

public class Subgroup : Entity
{
     public virtual string GroupNumber { get; set; }

     public virtual string RUSNumber { get; set; }

     public virtual string REANumber { get; set; }

     public virtual string CustomerType { get; set; }

     public virtual string Name { get; set; }

     public virtual IList<IndividualEmployment> Employees { get; set; }

     public virtual IList<BenefitsAdministrator> Administrators { get; set; }

     public virtual InvoicePreference InvoicePreference { get; set; }
}

Entity 2

public class InvoicePreference : IEntity
{
    public virtual Guid Id { get; set; }

    public virtual Guid SubgroupId { get; set; }

    public virtual bool PaperlessNotifications { get; set; }
}

Mapping 1

public static AutoPersistenceModel ConfigureSubGroup(this AutoPersistenceModel 
autoPersistenceModel)
{
    return autoPersistenceModel.Override<Subgroup>(map =>
    {
        map.Table("SubgroupV");

        map.Id(s => s.Id).Column(SubGroupPrimaryKeyColumn);

        map.Map(s => s.CustomerType, "BAS_Customer_Type");
        map.Map(s => s.RUSNumber, "BAS_RUS_Number");
        map.Map(s => s.GroupNumber, "BAS_Group_Number");
        map.Map(s => s.REANumber, "BAS_REA_Number");

        map.HasMany(s => s.Administrators).KeyColumn(SubGroupPrimaryKeyColumn);
        map.HasMany(s => s.Employees).KeyColumn(SubGroupPrimaryKeyColumn);
        map.HasOne(s => s.InvoicePreference).PropertyRef(i => i.SubgroupId);
    });
}

Mapping 2

public static AutoPersistenceModel ConfigureInvoicePreference(this AutoPersistenceModel autoPersistenceModel)
{
    return autoPersistenceModel.Override<InvoicePreference>(map =>
    {
        map.Table("SubgroupInvoicePreference");
        map.Schema(RetirementStatementsSchemaName);
    });
}
A-Dubb
  • 1,670
  • 2
  • 17
  • 22
  • Is this the exact message from NH profiler? – Stefan Steinegger Jul 13 '12 at 06:41
  • It's not the exact message. It has the actual name of the type. WARN: Disabled lazy properies fetching for Neb.RetirementStatements.Services.Domain.Subgroup beacuse it does not support lazy at the entity level – A-Dubb Jul 13 '12 at 14:46
  • Seems to be related to this question. http://stackoverflow.com/questions/4888140/fluent-nhibernate-references-and-propertyref-doing-a-select-with-lazy-load – A-Dubb Jul 13 '12 at 15:03

2 Answers2

2

InvoicePreference is referenced as hasone. Since it is lazyloaded by default NHibernate will create a proxy to populate the property InvoicePreference and to do that it needs the identity from InvoicePreference which is not present in the Subgroup. Therefor it has to query for it using the property in the propertyref.

To remedy that do .Not.LazyLoad() and/or .Fetch.Join()

Firo
  • 30,626
  • 4
  • 55
  • 94
  • I'm aware of how to do eager loading and the nuances of fetch strategies, but I actually don't want to do anything with that property/association. I'm not attempting to load it/access it at all. if that were the case, then yes I'd want to eager load it. – A-Dubb Jul 13 '12 at 14:47
  • I'm just wondering why it's specifically trying to lazy load that relationship. I'm not accessing that property or any others like Employees and Administrators. Why doesn't it do the same thing for the other two relationships? It should also be trying to lazy load Employees and Administrators. Is it the fact that the other 2 are collections and InvoicePreference is not? Shouldn't' they all behave the same way regardless? Up until the point that I access any of those properties, NHibernate should not try to load them. – A-Dubb Jul 13 '12 at 14:59
  • is this another way of saying what you explained? property-ref: Lazy loading only works over a objects id. When an property-association is resolved via a different column in the target entity, NH fetches it eagerly. Not that this wouldn't be possible, it's just not implemented: Bug – A-Dubb Jul 13 '12 at 15:10
  • it is not possible to lazy load it because (as i said) to create a proxy NH needs the **identity** of the referenced Entity which it doesn't have at this point. This is no bug but the compromise when using property-ref. use a select clause and project directly into the dto the properties you need – Firo Jul 14 '12 at 07:16
  • That's one approach to take, but at this point I'm just going to invert the association. Drop InvoicePreference from Subgroup, get rid of the mapping, and reference Subgroup inside of InvoicePreference. So I'll be refactoring SubgroupId from a Guid to an actual Subgroup type. It's the easiest and most effective thing for me to do at this point. Thanks for the recommendations though. – A-Dubb Jul 14 '12 at 07:28
2

I guess that there is some reason why NH disabled lazy loading "on entity level", which I understand as not creating proxies. There may be several reasons for that. Did you get another warning before? I don't really understand why it disabled "lazy properies", which means that some properties are lazy loaded. This is a feature that is used in the mapping explicitly, but I can't see something like this in your mapping definitions.

To overcome the N+1, you may use Fetch.Join. I had bad experience with that, because the queries get really large. In a complex model, you could hit some database server limits (like max. number of columns of a query). It is mostly better to use batch size, which reduces the number of queries notably. Take a look at my answer to "Nhinerbate lazy loading of reference entity".

Community
  • 1
  • 1
Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • Actually I don't want it to do eager loading at all. I don't want the entity loaded as I'm not using it for the request. I'm not even touching that property. I'm of course using AutoMapper, but it's not touching the property either as far as I can tell, because it's not being using in the mapping that's being executed. – A-Dubb Jul 13 '12 at 14:44
  • If you don't want eager loading, don't turn it off. Don't do Fetch.Join and use batch-size. I don't know why you get this warning. You may ask on an NH profiler forum / support site. – Stefan Steinegger Jul 16 '12 at 05:44
  • Will consider that. Thanks Stefan. – A-Dubb Jul 16 '12 at 18:08