<many-to-one> and access setting

Kenneth's Avatar

Kenneth

06 Apr, 2010 01:18 PM via web

Hi

According to http://nhforge.org/doc/nh/en/index.html#mapping-declaration-manytoone, NH supports the access setting, but it's ignored i Fluent NHibernate.

I have:
References(FluentNHibernateExtensions.Field("Subscription"))

            .Access.PascalCaseField(Prefix.Underscore)
            .Cascade.None();

(Dont't mind the Field method. It does the same as Reveal.Property, but with fields)

The output is:

<many-to-one cascade="none" class="..." name="Subscription">
  <column name="Subscription_id" />
</many-to-one>

Is this going to be supported?

Ken

  1. 2 Posted by Ryan on 11 Nov, 2010 02:54 PM

    Ryan's Avatar

    Same issue here, and it's driving me friggin nuts. I absolutely need this behavior to work. 7 months and no response from the fluent team? What gives?

  2. 3 Posted by ryan on 11 Nov, 2010 02:59 PM

    ryan's Avatar

    My problem is actually with a mapping.References, and FYI, I can't create a new ticket. Page is not found on post.

    Hello,
    I'm having an issue with setting Reference properties through their fields. It seems to simply ignore the Access settings on references, and helps itself to my setter. This is a bit of an issue.

    mapping.References(m => m.ToppingGroup).Access.CameCaseField(Prefix.Underscore);

    No effect. Still goes through the setter. Any advice?

    Thanks,
    Ryan

  3. Support Staff 4 Posted by James Gregory on 11 Nov, 2010 05:31 PM

    James Gregory's Avatar

    "What gives?" is that the bugs that people shout about the most are the ones that get fixed first. No shouting, no fixing until we run out of issues people are shouting about. This question may have been asked 7 months ago, but nobody has complained about it since and not even the original poster prompted us again. I admit it's not very good that we didn't respond, but where's the prerogative when the OP hasn't checked back in 7 months?

    As for your actual issue, I'll investigate it ASAP.

  4. Support Staff 5 Posted by James Gregory on 11 Nov, 2010 08:11 PM

    James Gregory's Avatar

    Right, I've just tried to reproduce your issue on our latest v1.x build and failed. My overrides are correctly setting the Access, and it's reaching the mapping.

    What version are you running? Have you tried upgrading?

  5. 6 Posted by Ryan on 11 Nov, 2010 08:37 PM

    Ryan's Avatar

    I tried upgrading to build 695, and got an Errors in Named Queries exception when spinning up the session factory. I'm using the current 1.x build from the main download link.

    I've dug a little deeper into the original issue, and it's only using the setter on derived classes.

    So, If I have two classes (both are concrete entity types), Product, and DerivedProduct where DerivedProduct inherits Product, the Reference properties are being set through the setters in the DerivedProduct only. In Product, it correctly uses the field accessor.

    If I add the same override for DerivedProduct, I get an InvalidOperationException because it tries to add a property that's already added.

  6. Support Staff 7 Posted by James Gregory on 11 Nov, 2010 08:59 PM

    James Gregory's Avatar

    Could you export your mappings and attach them here?

    There's an WriteMappingsTo method on AutoMap that you should be able to use.

    On Thu, Nov 11, 2010 at 8:39 PM, Ryan <
    ***@tenderapp.com<tender%***@tenderapp.com>
    > wrote:

  7. 8 Posted by Ryan on 11 Nov, 2010 09:17 PM

    Ryan's Avatar

    Here's the mapping for relevant part of the inheritance heirarchy. MenuItem>Product>ProductOverride

    You can see that in all of the reference properties in ProductOverride that are inherited from Product are missing the access attribute. It seems to be there for all of the simple value type properties. I haven't overridden any of the behaviors of the collection properties, but I can add them if you'd like to see if they exhibit the same issue.

    Thanks,
    Ryan

  8. Support Staff 9 Posted by James Gregory on 11 Nov, 2010 10:04 PM

    James Gregory's Avatar

    Well, something's not working right because your many-to-one's are being
    mapped in Product and ProductOverride, that shouldn't happen. What overrides
    have you got applied that produced this mapping?

    As an aside, this would be much easier using a convention rather than
    overriding everything.

    On Thu, Nov 11, 2010 at 9:19 PM, Ryan <
    ***@tenderapp.com<tender%***@tenderapp.com>
    > wrote:

  9. 10 Posted by Ryan on 11 Nov, 2010 10:18 PM

    Ryan's Avatar

    It is almost entirely handled through conventions. I have an override for one property on ProductOverride that doesn't exist on Product and doesn't follow the normal conventions, and about 5 properties on Product that don't follow the conventions. The overrides modify behavior of collections and map properties to columns whose names don't match. The only thing I'm having issues with are these reference properties on the entities that derive from other concrete entities.

  10. Support Staff 11 Posted by James Gregory on 12 Nov, 2010 10:09 AM

    James Gregory's Avatar

    Found it!

    Sorry, I mistakenly thought you were using overrides for everything and thus my tests didn't show anything failing.

    If you want to know the technical reasons for it not working, read on (otherwise skip the next few paragraphs):

    The conventions are designed to never overwrite any explicit changes you make. For example if you were using the ClassMap API, an access convention would never overwrite an explicit access strategy you defined for a property. Due to this behaviour we have to make sure that our "sensible defaults" we use when mapping are specified in such a way that they aren't mistaken for user defined values; unfortunately, there was a particular edge case in the automapping that incorrectly set a value in the same way a user would, thus causing the conventions to not overwrite that value.

    The particular bug only applies to setterless reference/many-to-one properties. The automapper call your IAutomappingConfiguration instance and asks the GetAccessStrategyForReadOnlyProperty method what it should use for that property; by default it would be BackField for an autoproperty and ReadOnlyPropertyThroughCamelCaseField otherwise. The value returned from this method would be set on the reference property in the (incorrect) manner which stops the conventions from changing it.

    I'm not in a position to commit a fix right now, but I'll be able to do it later tonight. You've got 3 options until then:

    1. If you're desperate, you can get the source and apply the changes yourself until I do
    2. You can wait until I'm able to commit the fix
    3. (this is probably the easiest) you can override the GetAccessStrategyForReadOnlyProperty(Member) method in your IAutomappingConfiguration and change it to return what you need. Conventions still wouldn't be able to overwrite the value from this method until I apply the fix, but at least it would get you going again.

    If you want to apply the fix yourself:

    1. Get the source either from the downloads page (make sure to get the latest v1.x "stable pre-release" source package) or from our v1.x branch on github.
    2. Find the ReferenceStep class, which is in src\FluentNHibernate\Automapping\Steps
    3. Locate lines #41-#42, which should look like this
    if (member.IsProperty && !member.CanWrite)
      mapping.Access = cfg.GetAccessStrategyForReadOnlyProperty(member).ToString();
    1. Change the second line (#42) to look like this:
    mapping.SetDefaultValue(x => x.Access, cfg.GetAccessStrategyForReadOnlyProperty(member).ToString());

    That's it, rebuild and you should be fine.

  11. 12 Posted by Ryan on 15 Nov, 2010 01:40 PM

    Ryan's Avatar

    Perhaps I'm doing something wrong, or perhaps we have a misunderstanding. I have overridden the GetAccessStrategyForREadOnlyProperty as suggested, and it had no effect. This is not a read only property, though. It is just a property that I need to bypass the setter logic during hydration of the object.

    Here is my override:
    public override FluentNHibernate.Mapping.Access GetAccessStrategyForReadOnlyProperty(FluentNHibernate.Member member)
    {

            return FluentNHibernate.Mapping.Access.CamelCaseField(CamelCasePrefix.Underscore);

    }

  12. Support Staff 13 Posted by James Gregory on 15 Nov, 2010 02:17 PM

    James Gregory's Avatar

    No matter, the fix is in. You should be able to update to the latest 1.x binary.

  13. 14 Posted by Ryan on 15 Nov, 2010 02:51 PM

    Ryan's Avatar

    OK, now with the latest build (1.2.0.691), I get the "Errors in named queries" exception when the session factory spins up. Nothing has changed in the named queries that I have set up. Do you know what has changed in that part of the code that I need to account for?

    Then, upon removing the line that loads the mappings for the named queries, and executing a unit test involving one of the entities in question, it still uses the setter for the property in question. Again, it's not a problem in Product, just ProductOverride. It does go through the field in Product, although, that isn't even really necessary.

  14. 15 Posted by Ryan on 16 Nov, 2010 01:16 PM

    Ryan's Avatar

    Am I downloading the wrong build? I know it wasn't the highest build number, but it was marked as the latest. The fix doesn't seem to change anything, aside from breaking things in other parts of the application.

  15. Support Staff 16 Posted by James Gregory on 16 Nov, 2010 01:54 PM

    James Gregory's Avatar

    Well in that case you're going to have to give me a more comprehensive example, because one of us is doing something wrong.

  16. 17 Posted by Ryan on 16 Nov, 2010 03:13 PM

    Ryan's Avatar

    OK, I'm attaching a sample project with a test to generate the mappings and a test that shows the stored procedure mapping errors. Let me know what you think.

  17. 18 Posted by Ryan on 16 Nov, 2010 03:18 PM

    Ryan's Avatar

    HMM, don't know what happened to the file. Does it not like zip files?

  18. Support Staff 19 Posted by James Gregory on 16 Nov, 2010 06:40 PM

    James Gregory's Avatar

    Got it the second time, thanks.

    On Tue, Nov 16, 2010 at 3:18 PM, Ryan <
    ***@tenderapp.com<tender%***@tenderapp.com>
    > wrote:

  19. 20 Posted by Ryan on 18 Nov, 2010 01:34 PM

    Ryan's Avatar

    Any new info?

  20. Support Staff 21 Posted by James Gregory on 18 Nov, 2010 11:24 PM

    James Gregory's Avatar

    The issue you're having with the named query is because of a breaking change since our last release, to work around it you need to remove the multiple calls to Mappings in your Fluent.Configuration and just use one. We'll fix that before the next release.

    After changing that, I'm not entirely sure I can replicate your issue. Could you check again after trying the above suggestion? If it still doesn't work, could you attach a schema? Using SchemaExport on my end doesn't seem to be working very well.

  21. 22 Posted by Ryan on 19 Nov, 2010 02:10 PM

    Ryan's Avatar

    That fixed the query problems, thank you.

    The issue with it mapping the references differently in subclasses of persistent classes still exists. If you look at the mapping for ConcreteClass.OutsideHeirarchy, and ConcreteClassOverride.OutsideHeirarchy, you'll see what I mean.

    I never went as far as to actually generate the schema or try to work with the entities. I was only interested in looking at the mappings that were generated. I have fixed the issues in the attached project, and you should be able to generate the schema now if you'd like.

    Thanks for all the help.
    --Ryan

  22. 23 Posted by Ryan on 22 Nov, 2010 08:28 PM

    Ryan's Avatar

    Any new info? Are you having any better luck with my example?

  23. 24 Posted by Ryan on 22 Nov, 2010 09:10 PM

    Ryan's Avatar

    OK, I've been trying some different things, and debugging, and I've noticed something I think is strange, and might help to point you to the bug. I'm not that familiar with the fluent code, but I have figured out what it happening.

    For reference properties, it only seems to be looking for mapping overrides or conventions on properties that are declared on the class in question. So, if it is declared in a superclass, it never inspects that property.

    The same does not seem to be true for HasMany, or HasManyToMany. They both seem to be inspecting properties regardless of where they are declared. I will start hunting with this in mind.

  24. 25 Posted by Ryan on 22 Nov, 2010 09:27 PM

    Ryan's Avatar

    Sorry, I totally dropped the ball here. My references were somehow all pointing back to the older version of the assembly. Everything works great now. Sorry for being a pest. Keep up the excellent work.

    Thanks,
    Ryan

Reply to this discussion

Preview Comments are parsed with Markdown. Help with syntax

Attached Files

    You can attach files up to 10MB

    What is the opposite of north?