I implemented what Paul suggested, although I had to do it slightly
differently and I'm not sure why.

public class User {

        private IList<Phone> _phones;

        /// <summary>
        /// A list of contact phone numbers.
        /// </summary>
        public virtual IEnumerable<Phone> Phones {
            get { return _phones; }
        }

        public virtual void AddPhone(Phone phone) {
            Check.Require(phone != null, "Phone cannot be left
undefined.");

            if (!_phones.Contains(phone)) {
                _phones.Add(phone);
            }
        }
}

The mapping for the property is:

            HasMany<Phone>(x => x.Phones)
                .Access.ReadOnlyPropertyThroughCamelCaseField
(Prefix.Underscore)
                .Table("Phone")
                .KeyColumn("UserId")
                .Cascade.All().Inverse()
                .AsBag();

If I added the get/set to the private IList<Phone> _phones, I received
an error indicating that it couldn't find the field "_phones".
However, now that I'm thinking about it, that might make sense because
having the get/set would indicate a propert and not a field.  I'm not
sure if the the Property access strategy would work and I believe that
you would also have to use the Reveal.Property<Entity>("Property")
method.  I also think that you would probably have to rename the
property to something like m_Phones.  I have not have too much luck
using the Reveal.Property method; sometimes it seems to work and other
times....not.

I also had to change the List<T> to IList<T>, otherwise to access
contains I would have to access it through the public IEnumerable
Phones instead of the private variable.  To add a new value, I would
need to access the private variable, but would receive an error
indicating an index out of range exception.  Changing the collection
from List<T> to IList<T> seems to have remedied that problem.

I'm going to continue testing to make sure that everything is cool.
It appears, though, that I have reached something that I can
definitely live with.

Thanks again,
Robert

On Jan 30, 6:53 pm, Paul Batum <[email protected]> wrote:
> I'm familiar with what AsReadOnly does - it used to be my preferred
> approach! Until I realised that compile time errors are preferable to
> runtime errors :)
>
> You could argue that a combination of both techniques is best - that way
> you're still exposing a readonly interface and preventing the casting
> problem. But if my developers are casting back to lists instead of using the
> appropriate methods (AddXXX, RemoveXXX), I've got bigger problems. I try to
> expose a public interface that makes the wrong things hard and the right
> things easy - I rarely go further than that. YMMV.
>
> On Sun, Jan 31, 2010 at 2:27 AM, Hudson Akridge 
> <[email protected]>wrote:
>
> > You can make the getter an IEnumerable for your property wrapper, but you
> > can still cast it back to a list and then make modifications to it.
> > .AsReadOnly() wraps it in a new read only collection that throws exceptions
> > whenever .Add/.Remove or any other collection modification methods are
> > attempted to be called. Otherwise all you're doing it providing a read only
> > interface, but not actually enforcing it.
>
> > Example:
> > public class Person
> >     {
> >         public Person()
> >         {
> >             _test = new List<string>();
> >         }
> >         private readonly IList<string> _test;
> >         public IEnumerable<string> Test
> >         {
> >             get { return _test; }
> >         }
> >     }
>
> > //Usage
> >             var tmp = new Person();
> >             var tmp2 = tmp.Test as List<string>;
> >             tmp2.Add("test");
>
> > tmp2 will have a count of 1 after this, and "test" will have been added.
> > This is typically not what you'd want a user of your model to be able to do.
>
> > On Fri, Jan 29, 2010 at 11:23 PM, Paul Batum <[email protected]> wrote:
>
> >> Not sure if I'm following entirely, but my approach to exposing
> >> collections is to make the getter an IEnumerable:
>
> >> public class Foo
> >> {
> >>   private List<Bar> _bars { get; set; }
>
> >>   public IEnumerable<Bar> Bars
> >>   {
> >>     get { return _bars; }
> >>   }
> >> }
>
> >> Then I map this using an access strategy:
>
> >>  public void Override(AutoMapping<Foo> mapping)
> >>      {
> >>          mapping.HasMany(x => x.Bars)
>
> >> .Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore)
> >>      }
>
> >> On Sat, Jan 30, 2010 at 4:20 AM, TheNephalim 
> >> <[email protected]>wrote:
>
> >>> I'm definitely going to look into the other strategies that you
> >>> mentioned.
>
> >>> One that I found, and seems to work, is the wrapper strategy that you
> >>> alluded to in item number 4.  I created a private property that is
> >>> revealed in the mapping and is accessed using the wrapper in a public
> >>> property.  When I tested two other properties that I had implemented
> >>> in this fashion, NHibernate determined that the collections were not
> >>> initialized using the assert that I mentioned in my previous posting
> >>> and only generated SQL if I directly accessed the collection directly,
> >>> i.e. User.Phones.Count.
>
> >>> Thank you for your response and confirming what I was thinking as well
> >>> as giving me some other avenues to pursue.
>
> >>> -Robert Eberhart
>
> >>> On Jan 29, 11:54 am, Hudson Akridge <[email protected]> wrote:
> >>> > You may want to toss your question over to the NHUsers google group,
> >>> but
> >>> > I'll give you my feedback for as much as It'll help ;)
>
> >>> > is this a trade off wherein we sacrifice encapsulation for performance
>
> >>> > This. As far as I'm able to gleen, that is the correct assumption to
> >>> make.
> >>> > There are ways around this.
> >>> > 1.) Don't access the collection unless you absolutely need it
> >>> > 2.) Look into doing Join Fetch's during your queries when you get the
> >>> data
> >>> > back if you know you're going to be using that collection as a result
> >>> of the
> >>> > query.
> >>> > 3.) Look into batch or subselect fetching, this is a good loading
> >>> strategy
> >>> > imo
> >>> > 4.) Look into lazy=extra. This allows you to do counts, contains, and a
> >>> few
> >>> > other common collection statements without loading the collection. You
> >>> will
> >>> > still have to access the backing collection, but you can write a
> >>> wrapper in
> >>> > your model. For example:
> >>> > public virtual int FastCountOfLogins()
> >>> > {
> >>> > return _logins.Count();
>
> >>> > }
>
> >>> > That will keep your collection lazy loaded until you actually need to
> >>> do
> >>> > something like a for each and iterate over it.
> >>> > 5.) Grab nhprofiler. This is one of the single best tools for finding
> >>> bottle
> >>> > necks in your application.
>
> >>> > On Fri, Jan 29, 2010 at 8:10 AM, TheNephalim <
> >>> [email protected]>wrote:
>
> >>> > > I have several issues that I'm working on, because I'm a newbie at
> >>> > > this, and wanted to address the one that I "solved" first.
>
> >>> > > The problem was that I was noticing that all of my collections were
> >>> > > not loading lazily, no matter what I did to mark them as such.  It
> >>> > > then dawned on me that the problem was the way I was returning the
> >>> > > collection.
>
> >>> > > For example,
>
> >>> > >        public virtual IList<Login> Logins {
> >>> > >            get { return new List<Login>(_logins).AsReadOnly(); }
> >>> > >            protected set { _logins = value; }
> >>> > >        }
>
> >>> > > I did it this way because if you just return _logins, you're actually
> >>> > > returning a reference to the private variable which violates
> >>> > > encapsulation and the whole reason for marking it private in the
> >>> first
> >>> > > place.
>
> >>> > > The problem is that the parent object, User, is a proxy.  Any time
> >>> > > that that parent object was accessed, for example, to set
> >>> > > User.LastName, a query for each of the collections was fired.  The
> >>> > > solution was to change the property to this:
>
> >>> > >        public virtual IList<Login> Logins {
> >>> > >            get { return _logins; }
> >>> > >            protected set { _logins = value; }
> >>> > >        }
>
> >>> > > I have run several tests in NUnit and watched the queries coming back
> >>> > > to know that this is what's happening.  Additionally, I used the
> >>> > > following:
>
> >>> > >        Assert.IsFalse(NHibernateUtil.IsInitialized(testUser.Logins));
>
> >>> > > It passes for the second property implementation and not the first.
>
> >>> > > The question I have is this:  Is there a way to encapsulate the
> >>> > > private variable but still have a proxy for lazy loading, or is this
> >>> a
> >>> > > trade off wherein we sacrifice encapsulation for performance?
>
> >>> > > Any help you can offer is appreciated.
>
> >>> > > Sincerely,
> >>> > > Robert Eberhart
>
> >>> > > --
> >>> > > You received this message because you are subscribed to the Google
> >>> Groups
> >>> > > "Fluent NHibernate" group.
> >>> > > To post to this group, send email to
> >>> [email protected].
> >>> > > To unsubscribe from this group, send email to
> >>> > > [email protected]<fluent-nhibernate%[email protected]>
> >>> <fluent-nhibernate%[email protected]<fluent-nhibernate%[email protected]>
>
> >>> > > .
> >>> > > For more options, visit this group at
> >>> > >http://groups.google.com/group/fluent-nhibernate?hl=en.
>
> >>> > --
> >>> > - Hudsonhttp://www.bestguesstheory.comhttp://twitter.com/HudsonAkridge
>
> >>> --
> >>> You received this message because you are subscribed to the Google Groups
> >>> "Fluent NHibernate" group.
> >>> To post to this group, send email to [email protected].
> >>> To unsubscribe from this group, send email to
> >>> [email protected]<fluent-nhibernate%[email protected]>
> >>> .
> >>> For more options, visit this group at
> >>>http://groups.google.com/group/fluent-nhibernate?hl=en.
>
> >>  --
> >> You received this message because you are subscribed to the Google Groups
> >> "Fluent NHibernate" group.
> >> To post to this group, send email to [email protected].
> >> To unsubscribe from this group, send email to
> >> [email protected]<fluent-nhibernate%[email protected]>
> >> .
> >> For more options, visit this group at
> >>http://groups.google.com/group/fluent-nhibernate?hl=en.
>
> > --
> > - Hudson
> >http://www.bestguesstheory.com
> >http://twitter.com/HudsonAkridge
>
> > --
> > You received this message because you are subscribed to the Google Groups
> > "Fluent NHibernate" group.
> > To post to this group, send email to [email protected].
> > To unsubscribe from this group, send email to
> > [email protected]<fluent-nhibernate%[email protected]>
> > .
> > For more options, visit this group at
> >http://groups.google.com/group/fluent-nhibernate?hl=en.

-- 
You received this message because you are subscribed to the Google Groups 
"Fluent NHibernate" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/fluent-nhibernate?hl=en.

Reply via email to