These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Always Wrap Collections?
PostPosted: Sat Nov 04, 2006 9:40 pm 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
in NHibernate 1.2.0 is there any reason why NHibernate can't provide the option of always wrapping existing collections? It seems to me that it would be very beneficial to provide support for wrapping a collection even when the containing type is being loaded from a persistent storage.

For example if I have an ISet<Bob> which happens to be declared as:


Code:

private ISet<Bob> myCol = new MyCoolCollection<Bob>()

public ISet<Bob> MyCollection
{
  get
  {
    return myCol;
  }
  set
  {
    myCol = value;
  }
}


Why couldn't NHibernate use the already existing MyCoolCollection<Bob>() and then wrap it's own PersistentGenericSet around that collection.

Instead of using the

Code:
override object Instantiate()


we could use

Code:
IPersistentCollection Wrap( ISessionImplementor session, object collection )


To me this seems like we would accomplish the same thing except now any custom logic we had in our cool collection could be used. Now I know there is custom collection support in 1.2.0, but the always wrap approach seems a lot easier to use/implement from a consuming developer's perspective, plus it provides additional support in that the class's constructor can be used to set properties on the collection without having to worry about NHibernate later swapping out it's configured collection for it's own.

Plus I have personally found the User Collection types to be very confusing. It's easy enough for non-generic collections which only work for one type, but once you have generic collections as your custom collection type the system seems to fight with you since you can not specify a generic collection type as the custom collection. So in the end you have to use a non-generic IUserCollectionType implementation which then gets stuck on the Instantiate() method.

So back to the point, is there any way we could add this as a feature/enhancement? Is there a reason why it is a bad idea and shouldn't be done? I'm not saying to force people to always wrap, but why can't we make it an option in the mapping file, something like always-wrap="true" where always-wrap defaults to false?


Top
 Profile  
 
 Post subject:
PostPosted: Sun Nov 05, 2006 9:07 am 
Contributor
Contributor

Joined: Wed May 11, 2005 4:59 pm
Posts: 1766
Location: Prague, Czech Republic
Submit this to JIRA, I'll consider it. No promises, though.

As for specifying a generic user type, it is possible, but you have to use the CLR syntax for generic instantiations, i.e. type="My.UserType`1[[Parameter.Type, Assembly]], Assembly".


Top
 Profile  
 
 Post subject:
PostPosted: Sun Nov 05, 2006 9:29 am 
Senior
Senior

Joined: Thu Feb 09, 2006 1:30 pm
Posts: 172
posted: http://jira.nhibernate.org/browse/NH-791

Sergey, what if I were to attempt to take on this enhancement myself? If I were able to complete it could it be merged into NHibernate? It does not seem like it would be that difficult, but you would obviously know better than I.

If it's something I could not do myself, everyone please go vote for it!


Top
 Profile  
 
 Post subject: Solution for Download....
PostPosted: Sun Jul 22, 2007 5:11 pm 
Newbie

Joined: Fri Sep 08, 2006 5:30 pm
Posts: 4
I have written some code that I believe does what you want assuming your custom collection inherits from an IList<T> . If that is too restrictive, it probably wouldn't be all that much more work.


I will be posting this on my BLOG at: http://damon.agilefactor.com in about an hour so check then.

Here is a description of the solution (not 100% but basically):


As I think many on this list know to have any kind of custom persistable collection you must inherit from PersistentGenericBag<T> which has no empty constructors (therefore you cannot use this to inherit on your main collection (!) as you MUST have an empty constructor for NHibernate to use).

We want our main custom collection to inherit from the concrete List<T> to use its core services as our base so you have already used your 1 concrete parent. (if we had multiple inheritance we could inherit from both, problem solved - kind of - almost - well another day.....).

Our interface must inherit from multiple sources for casting to work, as we need it to:

a) It must inherit from IList<T> as that is our baseline 'lowest common denominator' collection type

b) ALSO....(I see people report this as an error fairly often) for casting purposes, we need to be able to cast our item to PersistentGenericBag<T> so we must also inherit from the same interface. This crashes for people all the time who diligently create custom collections. Once you get Lazy, BAM1 Casting Exception. This solves that problem.

IPersistentCollection is the interface for this...


Solution (basic OO Knowledge really): Our primary wrapper concrete class has all the inherited items it requires (as I will show). Then only refer to this 'wrapper' class and use a combination of encapsulation and inheritence on a List<T>.

So there are 3 files:

1) internal class DomainPersistentGenericBag<T> : PersistentGenericBag<T>, IDomainList<T>


2) public interface IDomainList<T> : IList<T>, IPersistentCollection


NOTE2: I started by calling this IDomainPersistentGenericBag. This is perhaps more 'correct' but not as easy for the developers to sync the Interface and the concrete class as they do with IList and List (you'll see).


3) Finally our main Collection class, which we use all over the place quite easily:

public class DomainList<T> : List<T>, IUserCollectionType, IDomainList<T>


All the code is downloable from the blog below....


To use this simply:

1) In your Domain Class (I never use mapping files now, only attributes FYI):

public class MyDomainClass() {

...


private IDomainList<ArticleZones> _Zones;


[Bag(0, Inverse = true, CollectionTypeType = typeof(DomainList<ArticleZones>), Lazy = true, Generic = true, BatchSize = 10)]
[Key(1, Column = "articleid")]
[OneToMany(2, Class = "PMSiteDomainModel.ArticleZones, PMSiteDomainModel")]
public virtual IDomainList<ArticleZones> Zones
{
get { return _Zones; }
protected set { _Zones = value; }

}


Works like a charm!

You can refer to this collection as:

IDomainList<T> or DomainList<T>
IList<T> or List<T>

All work and you get full customization!

To download the FULL code, this will shortly be up on my blog as well as more details here:

http://damon.agilefactor.com



Thanks,
Damon Carr, CTO
agilefactor


Top
 Profile  
 
 Post subject: More Info....
PostPosted: Sun Jul 22, 2007 5:44 pm 
Newbie

Joined: Fri Sep 08, 2006 5:30 pm
Posts: 4
This class provides you with three ways to use what you describe (wrapping your existing collections):

1) Create a new instance of this class and then initialize it with your information

2) Same as above but use the returned value from your initialization (it is also wrapped in the original class you created)

3) Use an overload on the constructor to spin up a new version of this class, attached to your instance.

Here is some code:


public IPersistentCollection InitializeFromExisting(IList<T> collection)
{
InitializeFromExisting(collection, _session);

return _domainBadWrapped;

}

public IPersistentCollection InitializeFromExisting(IList<T> collection, ISessionImplementor session)
{
if (collection == null)
throw new ArgumentNullException("collection");

if (session != null)
_domainBadWrapped = new DomainPersistentGenericBag<T>(session, collection);
else
throw new NullReferenceException("No ISessionImplementor Set");

return _domainBadWrapped;
}


The constructor looks like:


public DomainList(IList<T> collection, ISessionImplementor session)
{
InitializeFromExisting(collection, session);
}


Here is the class signature:



public class DomainList<T> : List<T>, IUserCollectionType, IDomainList<T>

It relies on these two items:

public interface IDomainList<T> : IList<T>, IPersistentCollection
{
}



internal class DomainPersistentGenericBag<T> : PersistentGenericBag<T>, IDomainList<T>
{
public DomainPersistentGenericBag(ISessionImplementor session, IList<T> coll) : base(session, coll)
{
}
public DomainPersistentGenericBag(ISessionImplementor session) : base(session)
{
}
}


Again, code and article should be up shortly.
Thanks,
Damon


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.