Problem Mapping .NET ISet With Automapping

195 views
Skip to first unread message

Mohamed Meligy

unread,
Apr 30, 2011, 2:07:33 AM4/30/11
to fluent-n...@googlegroups.com
In this example I use .NET 4.0 HashSet. The properties are declared as "ISet" (from .NET not from Iesi), and use backing fields that are initialized to .NET 4.0 HashSet class. I'm using Jose's .NET 4.0 Set collection factory for this to work ( http://jfromaniello.blogspot.com/2011/03/using-in-mappings-without.html )
 
The example uses CollectionConvention to apply "Set" to all conventions. Hense, it worked correctly\.
I was trying to remove this (to allow more room for other types of collections) and had various errors, like "collection cannot be null. collection c" and so.
 
So, I investigated the FNH code and probably could get one or two possible problems.
 
(1) In the stuff that does build Set and check for Set there is no check for .NET ISet (only HashSet). I think this needs to be added.
 
(2) There is somewhere in the code mapping steps you build new instances of collections, after few checks (that don't include the .NET ISet check of course), you default to .NET List<> for the collection. So, if I change the declarations of my props/fields to HashSet not ISet to satisfy (1), I get an error that it cannot assign generic List (I think it's referring to List<>) to HashSet. So, I think it needs to check for ISet/HashSet as well.
 
I haven't tested whether the suggested fixes the problem as I was more bothered with how I can override this without messing too much with FNH stuff.
I tried to make various checks inside CollectionConvention and CollectionConventionAcceptance just to be able to read the type of the property and compare it to typeof(ISet<>) so that then only I do use IsSet(), but I didn't find a way to get that type.
Also, tried to find something that I can override in StoreConfiguration to fix this, the only thing that sounded promising there was building autompaiing steps, and then felt like I'm not really sure I should mess with this part.
 
Any ideas/fixes?
 
 
Thanks a lot.
 
Regards,

Mohamed Meligy
Readify | Senior Developer

M:+61 451 835006 | W: readify.net

Description: Description: Description: Description: rss_16  Description: Description: Description: Description: cid:image003.png@01CAF81D.6A076510  Description: Description: Description: Description: cid:image005.png@01CAF81D.6A076510

James Gregory

unread,
May 1, 2011, 4:43:49 AM5/1/11
to fluent-n...@googlegroups.com
Hi Mohamed,

What version of FNH are you running?

1) Definitely sounds like an oversight. Ideally there should be a way for the user (yourself...) to control what FNH considers a Set, rather than us having a hard-coded list of collection types.

2) What part of the code is this? Even though FNH doesn't recognise ISet, it definitely does work with HashSet.

You should be able to use HashSet in the place of ISet and have it work. It's not ideal, but it should work until we can get a fix out.

Mohamed Meligy

unread,
May 1, 2011, 6:41:56 AM5/1/11
to fluent-n...@googlegroups.com
1. It's latest NuGet package. Not sure how outdated / recent this would be.
 
3. These are the places I thought might be relevant in FNH src 1.x branch sounded like latest modified):
 
a. fluent-nhibernate\src\FluentNHibernate\Mapping\CollectionTypeResolver.cs line: 25-33:

        static bool IsSet(Member member)
        {
            return member.PropertyType == typeof(ISet|| member.PropertyType.Closes(typeof(ISet<>)) || member.PropertyType.Closes(typeof(HashSet<>));
        }
 
        static bool IsEnumerable(Member member)
        {
            return member.PropertyType == typeof(IEnumerable|| member.PropertyType.Closes(typeof(IEnumerable<>));
        }
 
b. fluent-nhibernate\src\FluentNHibernate\Testing\Values\List.cs line 22-62
This one is just test code, but it makes me suspect there is something in FNH that is adhering to these rules that are being tested and this could be why I'm getting the exception when using HashSet directly instead of ISet that it says trying to assign List<> iinstead to the prroperty:
(Of course ISet below is the Iesi one) 
        public override Action<T, AccessorIEnumerable<TListElement>> ValueSetter
        {
            get
            {
                if (_valueSetter != null)
                {
                    return _valueSetter;
                }
 
                return (target, propertyAccessor, value) =>
                {
                    object collection;
 
                    // sorry guys - create an instance of the collection type because we can't rely
                    // on the user to pass in the correct collection type (especially if they're using
                    // an interface). I've tried to create the common ones, but I'm sure this won't be
                    // infallible.
                    if (propertyAccessor.PropertyType.IsAssignableFrom(typeof(ISet<TListElement>)))
                    {
                        collection = new HashedSet<TListElement>(Expected.ToList());
                    }
                    else if (propertyAccessor.PropertyType.IsAssignableFrom(typeof(ISet)))
                    {
                        collection = new HashedSet((ICollection)Expected);
                    }
                    else if (propertyAccessor.PropertyType.IsArray)
                    {
                        collection = Array.CreateInstance(typeof(TListElement), Expected.Count());
                        Array.Copy((Array)Expected, (Array)collection, Expected.Count());
                    }
                    else
                    {
                        collection = new List<TListElement>(Expected);
                    }
 
                    propertyAccessor.SetValue(target, collection);
                };
            }
            set { _valueSetter = value; }
        }
 
 
Last, when you are saying it should work with HashSet used for declarations instead of .NET ISet, do you try it using the same collection factory I refered to in previous message or some other mean?
 
 
Thanks a lot, James :) 

 Modt Ragrds,

Mohamed Meligy
Readify | Senior Developer

M:+61 451 835006 | W: readify.net

Description: Description: Description: Description: rss_16  Description: Description: Description: Description: cid:image003.png@01CAF81D.6A076510  Description: Description: Description: Description: cid:image005.png@01CAF81D.6A076510
--
You received this message because you are subscribed to the Google Groups "Fluent NHibernate" group.
To post to this group, send email to fluent-n...@googlegroups.com.
To unsubscribe from this group, send email to fluent-nhibern...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/fluent-nhibernate?hl=en.

James Gregory

unread,
May 1, 2011, 6:55:20 AM5/1/11
to fluent-n...@googlegroups.com
CollectionTypeResolver definitely looks like the place we should be changing, and ideally adding a callout to something configurable.

Your second example isn't anything to worry about, that's a part of the PersistenceSpecification stuff (a testing tool for mappings). It's not actually used to generate mappings. You're correct in that it wouldn't work, but it won't affect your mappings in any way.

HashSet is a .Net 3.5 collection, so the collection factory stuff isn't necessary. It should just work straight out of the box.

Mohamed Meligy

unread,
May 1, 2011, 8:09:54 AM5/1/11
to fluent-n...@googlegroups.com
OK, then I did replace all ISet<> with HashSet<>. Also, commented the collection factory line.
 
The configuration passes, on save, I get the following error:
 
{"Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericSet`1[FluentNHSampleApp.Domain.Customization]' to type 'System.Collections.Generic.HashSet`1[FluentNHSampleApp.Domain.Customization]'."}
 
Relevant classes (skipping EntityBase that keeps the Id):
 
    public class Product : EntityBase
    {
        private readonly HashSet<Customization> customizations;
 
        public Product()
        {
            customizations = new HashSet<Customization>();
        }
 
        public virtual string Name { getset; }
        public virtual decimal Price { getset; }
 
        public virtual HashSet<Customization> Customizations
        {
            get { return customizations; }
        }
    }
 
    public class Customization : EntityBase
    {
        private readonly HashSet<string> possibleValues;
 
        public Customization()
        {
            possibleValues = new HashSet<string>();
        }
 
        public virtual string Name { getset; }
 
        public virtual HashSet<string> PossibleValues
        {
            get { return possibleValues; }
        }
    }
Supposedly relevant generated XML mapping:
    <set access="nosetter.lowercase" cascade="all" name="Customizations">
      <key>
        <column name="Product_id" />
      </key>
      <one-to-many class="FluentNHSampleApp.Domain.Customization, FluentNHSampleApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </set>

Here is what the Save code looks like (copy paste from Jose's orking confORM example):
            using (ITransaction tx = s.BeginTransaction())
            {
                var product = new Product
                                  {
                                      Name = "Fideos",
                                      Customizations =
                                          {
                                              new Customization
                                                  {
                                                      Name = "Tuco",
                                                      PossibleValues = {"Pocon""Medio""Sopa"}
                                                  }
                                          }
                                  };
 
                s.Save(product);
 
                tx.Commit();
            }

 Any ideas? Thanks again.

Mohamed Meligy
Readify | Senior Developer

M:+61 451 835006 | W: readify.net

Description: Description: Description: Description: rss_16  Description: Description: Description: Description: cid:image003.png@01CAF81D.6A076510  Description: Description: Description: Description: cid:image005.png@01CAF81D.6A076510

--

James Gregory

unread,
May 1, 2011, 12:30:58 PM5/1/11
to fluent-n...@googlegroups.com
Ugh, that's not good. Sorry Mohamed, but it doesn't look like there's going to be an easy way to get this to work without using Iesi.Collections. I'll get a fix out ASAP to get it working with the native .Net 4 collection types, no hackery required.

Mohamed Meligy

unread,
May 1, 2011, 1:04:10 PM5/1/11
to fluent-n...@googlegroups.com
hmm.. It's OK. I guess I'll keep the example the way it is for now.
 
Glad to know you'll handle it soon anyway.
 
Thanks a lot, James, you rock :)

 Regards,

Mohamed Meligy
Readify | Senior Developer

M:+61 451 835006 | W: readify.net

Description: Description: Description: Description: rss_16  Description: Description: Description: Description: cid:image003.png@01CAF81D.6A076510  Description: Description: Description: Description: cid:image005.png@01CAF81D.6A076510
On Mon, May 2, 2011 at 2:30 AM, James Gregory <jagregory.com@gmail.com> wrote:
Ugh, that's not good. Sorry Mohamed, but it doesn't look like there's going to be an easy way to get this to work without using Iesi.Collections. I'll get a fix out ASAP to get it working with the native .Net 4 collection types, no hackery required.

--

James Gregory

unread,
May 1, 2011, 2:32:43 PM5/1/11
to fluent-n...@googlegroups.com
If you were happy to use Iesi, then it would all work fine ;)

But yeah, it's about time we supported the native .Net 4 collections. I'll get a fix out soon.

James Gregory

unread,
May 1, 2011, 2:33:08 PM5/1/11
to fluent-n...@googlegroups.com
For what it's worth, you're not the only one who's requested native ISet support either.

Mohamed Meligy

unread,
May 1, 2011, 8:00:20 PM5/1/11
to fluent-n...@googlegroups.com
Yeah, I can guess that (would feel bad if I were, LOL).
 
I need the example the way it is as it needs to be as close to Jose's EF/confORM examples as pssoible, as the main idea of creating mine was porting his confORM one to FNH.
 
That's why will keep it the way it is for now and update it once native ISet is supported.

Thanks,

Regards, 

Mohamed Meligy
Readify | Senior Developer

M:+61 451 835006 | W: readify.net

Description: Description: Description: Description: rss_16  Description: Description: Description: Description: cid:image003.png@01CAF81D.6A076510  Description: Description: Description: Description: cid:image005.png@01CAF81D.6A076510
On Mon, May 2, 2011 at 4:33 AM, James Gregory <jagregory.com@gmail.com> wrote:
For what it's worth, you're not the only one who's requested native ISet support either.

--

James Gregory

unread,
May 12, 2011, 3:35:02 PM5/12/11
to fluent-n...@googlegroups.com
v1.x branch now should have a fix for this. Because we're not a .Net for project yet (and I can't yet be bothered to maintain two versions), FNH will just pick up any type named ISet<T> and assume you know what you're doing. Let me know how it turns out when you've had a chance to take a look.

Mohamed Meligy

unread,
May 12, 2011, 10:42:18 PM5/12/11
to fluent-n...@googlegroups.com
LOL, nice workaround. Reminds me with IHtmlString in MVC :) -but when you don't even have as much control :D
 
Will try it tonight and tell you.
 
If it works nicely, will there be a possibility to have that on Nuget? Like 1.11?
(I know, acting greedy)

Thanks a lot James,

 

Regards. 

Mohamed Meligy
Readify | Senior Developer

M:+61 451 835006 | W: readify.net

Description: Description: Description: Description: rss_16  Description: Description: Description: Description: cid:image003.png@01CAF81D.6A076510  Description: Description: Description: Description: cid:image005.png@01CAF81D.6A076510
On Fri, May 13, 2011 at 5:35 AM, James Gregory <jagregory.com@gmail.com> wrote:
v1.x branch now should have a fix for this. Because we're not a .Net for project yet (and I can't yet be bothered to maintain two versions), FNH will just pick up any type named ISet<T> and assume you know what you're doing. Let me know how it turns out when you've had a chance to take a look.

--

James Gregory

unread,
May 13, 2011, 4:13:16 AM5/13/11
to fluent-n...@googlegroups.com
I'll get a few more bugs fixed, then I'll push a new release to nuget.

James Gregory

unread,
May 13, 2011, 4:13:52 AM5/13/11
to fluent-n...@googlegroups.com
I also just realised I said ".Net for" instead of ".Net 4"... Can you tell I was working late last night? ;)

Mohamed Meligy

unread,
May 13, 2011, 7:41:16 PM5/13/11
to fluent-n...@googlegroups.com
Thanks a lot, James.
Worked great in my sample app.

Waiting for the Nuget update to include it in the post.

Thanks again,

Regards,

Mohamed Meligy
Readify | Senior Developer

M:+61 451 835006 | W: readify.net

Description: Description: Description: Description: rss_16  Description: Description: Description: Description: cid:image003.png@01CAF81D.6A076510  Description: Description: Description: Description: cid:image005.png@01CAF81D.6A076510



On Fri, May 13, 2011 at 6:13 PM, James Gregory <jagregory.com@gmail.com> wrote:
I also just realised I said ".Net for" instead of ".Net 4"... Can you tell I was working late last night? ;)

--

Dawid Ciecierski

unread,
Jul 13, 2012, 3:31:30 AM7/13/12
to fluent-n...@googlegroups.com
Apologies for coming back to such an old issue, but I came across the same thing with a recent version of FNH (1.3.0.733) and NH (3.3.1.4000), both from nuget. I tried to make Jose's Net4Collections work by plugging it via:

ExposeConfiguration(c => c.SetProperty(
  Environment.CollectionTypeFactoryClass,
  typeof(Net4CollectionTypeFactory).AssemblyQualifiedName))


...but sadly that didn't work, resulting in the error "Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericSet`1[...]' to type 'System.Collections.Generic.ISet`1[...]". I then came across this thread, and as far as I can understand from what you're saying, and with my (very) limited knowledge of FNH & NH, ISet<T> from System.Collections.Generic should just work out of the box, with no need for Net4Collections... is that correct?

Is there any way to persuade FNH & NH to play nice with System.Collections.Generic?

Regards,
Dawid Ciecierski


On Saturday, May 14, 2011 1:41:16 AM UTC+2, Mohamed Meligy wrote:
Thanks a lot, James.
Worked great in my sample app.

Waiting for the Nuget update to include it in the post.

Thanks again,

Regards,

Mohamed Meligy
Readify | Senior Developer

M:+61 451 835006 | W: readify.net

Description: Description: Description: Description: rss_16  Description: Description: Description: Description: cid:image003.png@01CAF81D.6A076510  Description: Description: Description: Description: cid:image005.png@01CAF81D.6A076510



On Fri, May 13, 2011 at 6:13 PM, James Gregory <jagregory.com@gmail.com> wrote:
I also just realised I said ".Net for" instead of ".Net 4"... Can you tell I was working late last night? ;)

--
You received this message because you are subscribed to the Google Groups "Fluent NHibernate" group.
To post to this group, send email to fluent-nhibernate@googlegroups.com.
To unsubscribe from this group, send email to fluent-nhibernate+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages