Your thoughts on why to just expose it out as an ienumerable make
sense. There's positives and negatives to both. Wrapping it in a
ReadOnlyCollection initializes it, as well as prevents you from ever
being able to take advantage of lazy=extra.
On Mon, Feb 1, 2010 at 6:37 PM, Paul Batum <[email protected]>
wrote:
Yes, sorry, there was an error in my code. You can map the member as
either a field or a property, but you have to use the appropriate
access mechanism. ReadOnlyPropertyThroughCamelCaseField tells
NHibernate to use the mapped property when getting the value, and to
look for a field with camel case naming and to use that when setting
the value. If the underlying member is an autoproperty, then it will
fail to find a matching field.
On Tue, Feb 2, 2010 at 8:17 AM, TheNephalim
<[email protected]> wrote:
>
> 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
.
>
--
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
.
--
- 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]
.
For more options, visit this group at http://groups.google.com/group/fluent-nhibernate?hl=en
.