views:

348

answers:

3

I've got a class something like this:

public class Account
{
 public virtual int Id { get; private set; }
 public virtual string Username { get; set; }

 [EditorBrowsable( EditorBrowsableState.Never )]
 public virtual string Password { get; private set; }

 public void SetPassword( string password ){ ... }
 public bool CheckPassword( string password ) { ... }
}

I've set it up this way since I don't ever want the Password property used directly in code that uses the Account type. The Account map looks something like this:

public class AccountMap : ClassMap<Account>
{
 public AccountMap()
 {
  Id( x => x.Id );
  Map( x => x.Username );
  Map( x => x.Password );
 }
}

When I actually use this with NHibernate I get an InvalidProxyTypeException

NHibernate.InvalidProxyTypeException: The following types may not be used as proxies:
 Fringine.Users.Account: method SetPassword should be virtual
 Fringine.Users.Account: method CheckPassword should be virtual

I understand that NHibernate is trying to create a proxy class to support lazy loading and that I can either mark the methods as virtual add a Not.LazyLoad() to the map to resolve the exception. But - I don't want to do either of those. I want to support lazy loading but I don't see why those methods need to be virtual.

Does NHibernate (or Castle internally) evaluate the contents of the method to determine which fields are used and optimize the lazy loading for those properties? If not, why does the method need to be virtual if all the properties are and they'll be lazy-loaded when they're referenced by the method.

Is there a way to exclude certain methods from the virtual requirement?

A: 

I've noticed that NHibernate heavily relies on POCOs for all of its entities. For better or worse, I haven't run across any scenario where someone has broken that convention. Davy Brion explains it in great detail here. (He references your point that, yes, you can mark classes as not lazy load. But, since nHibernate then won't create any of your proxies, you're stuck.)

I don't know if this is helpful, but that's how Castle does it. Now that (if you're using 2.1) you're able to choose which proxy generator to use, moving to one of the other choices might allow you to generate proxies in a way that suits your needs.

joshua.ewer
A: 

You can desactivate lazy-loading at the class level and activate it on the property by property basis,the lazy loading is often use for collection-assocation relationships.

public class AccountMap : ClassMap<Account>{        
    public AccountMap()
    {
        Not.LazyLoad();
        Id( x => x.Id );
        Map( x => x.Username ).LazyLoad();
        Map( x => x.Password );        
    }
}

Or you can try with a private field and a "field" strategy for it :

public class AccountMap : ClassMap<Account>{        
    public AccountMap()
    {

        Id( x => x.Id );
        Map( x => x.Username )
        Map( x => x.Password).Access.AsCamelCaseField();         
    }
}

public class AccountMap : ClassMap<Account>{        
   private string password;
   public string Password{ get; }
}
Matthieu
Is there anything similar to Map( x => x.Method() ).Not.LazyLoad()?
Paul Alexander
you can also desactivate proxy generation ckeck with this property : use_proxy_validator to false
Matthieu
+1  A: 

The reason is that you could access fields in your methods, which will not be initialized. So the easiest way is to load the contents of the entity on any call to the object (the only exception is the access to the id, which is already available in the proxy).

So you can safely implement your methods as if there weren't proxies - with the trade-off that the method needs to be virtual (which - I agree - is not perfect).

If you think that this is a problem for your class design, try to move the functionality to another class (eg. PasswordManager, PasswordValidator etc). This class will aggregate the Account or take it it as argument, so the Account will only be loaded when this class actually calls one of its properties.

Stefan Steinegger