2

I have a class model that looks like this:

public class TheSystem 
{
    public virtual Guid Id { get; protected set; }
    public virtual ICollection<User> Users { get; protected set; }
}

public class User
{
    public virtual Guid Id { get; protected set; }
    public virtual string Username { get; set; }
    ...
}

The mappings

public class TheSystemMap : ClassMap<TheSystem>
{
    public TheSystemMap()
    {
        Id(x => x.Id).GeneratedBy.GuidComb();
        HasMany(x => x.Users).Cascade.AllDeleteOrphan().ExtraLazyLoad().Cache.ReadWrite().IncludeNonLazy();

        Cache.ReadOnly();
    }
}

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.Id).GeneratedBy.GuidComb();
        Map(x => x.Username).Not.Nullable();
        ...

        Cache.ReadOnly();
    }
}

The problem arrives when I want to add a new user to the system. Because of I'm referencing the Users collection, NHibernate loads all the instances (which I don't want, because I just want to insert the single user).

...
theSystem.Users.Add(new User("aUser"));
...

The ExtraLazyLoad() option is working as expected when I try to get the item's count (The code next produces to query only for the count)

...
var count = theSystem.Users.Count;
...

I've also try adding .AsBag() option with the same result.

I'm missing something in the mapping or is a situation that can't be solved the regular ways?

Najera
  • 2,869
  • 3
  • 28
  • 52
Agustin Meriles
  • 4,866
  • 3
  • 29
  • 44

2 Answers2

2

If you're doing a model like this, chances are you're looking to have an API surface that constrains its consumers so they can only perform valid operations. When I do this, I generally enforce complete public immutability of the entities. All public operations that change state of any kind are mediated through methods or objects modelling valid business actions that can be validated and checked. Then there's an internal API that is less constrained, and direct access to the persistence mechanism is available at this level.

In this model, collections are exposed at the public API surface as IEnumerable<T> and mapped to the underlying fields, and I add methods to the relevant controlling entities to create/add and remove child objects (provided this is a valid domain operation, of course). Something like

theSystem.AddUser(user);

and

theSystem.RemoveUser(user);

Though in point of fact my add method will most likely look like

theSystem.AddUser(email, password, confirmPassword, firstName, lastName)

since I don't allow API consumers to construct any persisted entity directly. The AddUser method constructs the user, saves it to the session, and adds it to the Users collection. Or if there's a cascade (which I generally avoid), just adds it to the collection.

(Doesn't work well with DI, of course, but then again that's not the sort of API I'm building here. If there's DI going on it happens lower down.)

Neil Hewitt
  • 2,518
  • 18
  • 24
  • 1
    I'm not sure that this solves the problem of not loading the entire collection. When you add it to the collection it will lazy load the entire collection of users. – Cole W May 06 '14 at 15:39
  • Hmm. Good point. What I didn't mention above is that for 'potentially infinite' collections - the sort I never want to load all of at any one time - I have a different solution which involves not mapping directly, and so this isn't a problem for me. But it would be for my solution above. I'd say a combination of the 'hide it behind a method' approach along with your suggestion to just add directly to the session is thus the best solution here... – Neil Hewitt May 06 '14 at 16:17
  • I talked about my 'infinite collection' solution here, BTW, if it's of any use: http://stackoverflow.com/questions/14334618. – Neil Hewitt May 06 '14 at 16:24
1

You could just create a new User and avoid TheSystem all together:

User newUser = new User();
session.Save(newUser);
Cole W
  • 15,123
  • 6
  • 51
  • 85
  • All right, I know that this way will accomplish the task, but I'm using a persistance by reachability model, and persistence ignorance. So theoricatelly, I have no access to session and the inserts must be by referencing objects or adding items to collections. – Agustin Meriles May 05 '14 at 19:41
  • I'm not sure how persistance ignorance plays into this. You have to have a way of saving entities today. Use that same model for creating Users. Don't get too caught up in the example above. I gave it to you in NHibernate terms. I'm not familiar with whatever wrappers you have in place. – Cole W May 05 '14 at 19:46
  • I finally go for this way, retrieving the session and saving the instance. Is the best option for now. – Agustin Meriles May 06 '14 at 16:40