Accessing Id as public field?

9 views
Skip to first unread message

anthony

unread,
Sep 12, 2009, 1:33:04 PM9/12/09
to Fluent NHibernate
I have an existing class Foo that I would like to persist "as is". By
this, I mean not modifying how the class is implemented. Rather than a
public vitual int Id property, Foo has a public int Id field. I had
hoped to be able to map this using Id(x => x.Id).Acess.AsField(), but
doing so results in an InvalidCastException.

Is there a way to use the public int Id as the mapped Id for a
persistent object?

Here's my code:

public class Foo
{
public int Id;
}

public class FooMap : ClassMap<Foo>
{
public FooMap()
{
Id(x => x.Id).Access.AsField();
}
}

private static void BuildSchema(Configuration config)
{
new SchemaExport(config).Create(false, true);
}

var config = Fluently.Configure().
Database(SQLiteConfiguration.Standard.UsingFile(file).ShowSql()).
Mappings(m => m.FluentMappings.AddFromAssemblyOf<Foo>());
config.ExposeConfiguration(BuildSchema);

this.factory = config.BuildSessionFactory();

and here's the resulting exception:

[nunit2] ----> FluentNHibernate.Cfg.FluentConfigurationException :
An invalid or incomplete configuration was used while creating a
SessionFactory. Check PotentialReasons collection, and InnerException
for more detail.
[nunit2]
[nunit2]
[nunit2] ----> System.Reflection.TargetInvocationException :
Exception has been thrown by the target of an invocation.
[nunit2] ----> System.InvalidCastException : Cannot cast from
source type to destination type.
[nunit2] at
FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory ()
[0x00000]

Paul Batum

unread,
Sep 13, 2009, 3:26:01 AM9/13/09
to fluent-n...@googlegroups.com
Hi Anthony,

Unfortunately, fluent nhibernate doesn't yet support direct mapping of fields. This is because there is significant chunk of code that works with PropertyInfos that we haven't managed to revisit yet. You can use AsField() when you have a property that exposes a field and you want NHibernate to set the field directly, but this does not help your case where all you have is a public field. I am afraid that unless there is a workaround that I'm unaware of, you will have to map your entity manually with xml.

Paul Batum

Anthony Gatlin

unread,
Sep 13, 2009, 5:37:46 AM9/13/09
to Fluent NHibernate
Paul and Anthony T.,

Apologies but I am a little confused by the discussion regarding
above. I just want to see if I can understand this for my own
edification.

Paul, when a class contains an Id field instead of a property, I
assume that NHibernate will not map it since fields cannot be virtual.
I am unfamiliar with the AsField() method. It looks the AsField() is
just a workaround to allow you to map a field. However, I think I am
understanding that while this can be used to map most fields, AsField
() can not be used in place of the primary Id property. You seem to be
suggesting that with some of your further refactoring, that you may
modify this behavior. Is this correct?

Anthony T., I am just curious, is there a technical reason why you may
not want to the change fields to properties? I understand that for
rare cases (e.g. high frequency loops), a field may offer a
performance benefit. However for 99% of cases, most developers prefer
properties. For items without default values, declaring a property or
a field (in.NET 3.x) requires virtually the same effort.

Field: public int Id;
Property: public int Id { get; set; }

(Oh crap. I just stepped into a pile of droppings from the fields vs.
properties debate.)


On Sep 13, 2:26 am, Paul Batum <paul.ba...@gmail.com> wrote:
> Hi Anthony,
>
> Unfortunately, fluent nhibernate doesn't yet support direct mapping of
> fields. This is because there is significant chunk of code that works with
> PropertyInfos that we haven't managed to revisit yet. You can use AsField()
> when you have a property that exposes a field and you want NHibernate to set
> the field directly, but this does not help your case where all you have is a
> public field. I am afraid that unless there is a workaround that I'm unaware
> of, you will have to map your entity manually with xml.
>
> Paul Batum
>

Paul Batum

unread,
Sep 13, 2009, 7:43:48 AM9/13/09
to fluent-n...@googlegroups.com
Hi Anthony G,

NHibernate doesn't require that your members be virtual - but class level lazy loading does. If you turn off class level lazy loading then you can get by with non virtual members just fine, and you could still use lazy loaded collections.

NHibernate has the concept of access strategies. Basically these are set of strategies for how NHibernate sets the values on an object. The default access modifier is property, but field is also avaliable. These access modifiers typically use a naming convention to find the corresponding field for a property. For example, if I have the following field and property pair:

private string _string1;

public string String1
{
  get { return _string1; }
  set { _string1 = value; }
}

I can tell NHibernate to use the underlying field by specifying an access strategy of "field.camelcase-underscore". We expose this functionality in Fluent NH with the Access property. The equivalent in FNH would be:

Map(x => x.String1)
  .Access.CamelCaseField(Prefix.Underscore)

Now if you are using NHibernate xml mapping files, you can map a field directly. See (just scroll down a bit and take a look at table 5.1, read the description of the field strategy):
http://www.nhforge.org/doc/nh/en/index.html#mapping-declaration-property

Unfortunately FNH is limited to mapping properties and using access strategies. This means that if you ONLY have a field, you can't use FNH to map it. As I mentioned in the previous email, this limitation is due to our reliance on PropertyInfo - which should be resolved eventually as the code is refactored.

Hope that helps clears things up.

Paul Batum

anthony

unread,
Sep 14, 2009, 4:23:40 PM9/14/09
to Fluent NHibernate
> Anthony T., I am just curious, is there a technical reason why you may
> not want to the change fields to properties? I understand that for
> rare cases (e.g. high frequency loops), a field may offer a
> performance benefit. However for 99% of cases, most developers prefer
> properties. For items without default values, declaring a property or
> a field (in.NET 3.x) requires virtually the same effort.

I would like to persist objects of a class that is defined in a 3rd
party library, and that has a public int field that serves as a
naturally unique Id. In other words, I'm using the class as is and
don't have the capacity to modify it directly.

Obviously I'm using virtual properties for persistent classes that
I've defined myself.

Thanks for clarifying that FluentNHibernate expects to deal with
PropertyInfo. I'll likely build an xml mapping for the 3rd party class
in question and Fluent everywhere else.

--anthony

Anthony Gatlin

unread,
Sep 14, 2009, 10:32:17 PM9/14/09
to Fluent NHibernate
Anthony T.,

That's a pretty interesting idea--persisting objects from third party
libraries. I can think of a handful of cases right off the bat where
that might come in really handy. If FNH could auto-map these items and
persist them, that would be awesome. I think I am going to unashamedly
borrow this from you. :)

One idea. If the classes are not sealed, perhaps you can extend them
in your own app. By extending them, you could make them completely NH
compatible. When you needed to pass them back to the calling
application, you could cast the objects back to their original type.
Would this be of any value?

Anthony G.

anthony

unread,
Sep 15, 2009, 3:23:22 AM9/15/09
to Fluent NHibernate


On Sep 14, 7:32 pm, Anthony Gatlin <gatl...@gmail.com> wrote:
> Anthony T.,
>
> That's a pretty interesting idea--persisting objects from third party
> libraries. I can think of a handful of cases right off the bat where
> that might come in really handy. If FNH could auto-map these items and
> persist them, that would be awesome. I think I am going to unashamedly
> borrow this from you. :)

i'm not using auto-mapping here, just want to be able to take classes
that were not necessarily designed with persistence in mind and map
them manually.

>
> One idea. If the classes are not sealed, perhaps you can extend them
> in your own app. By extending them, you could make them completely NH
> compatible. When you needed to pass them back to the calling
> application, you could cast the objects back to their original type.
> Would this be of any value?

This is in fact exactly what I'm doing now, but it feels somewhat
extraneous.

Clearly great minds think, and are named, alike.

--anthony

Mikael Henriksson

unread,
Sep 15, 2009, 4:42:13 AM9/15/09
to fluent-n...@googlegroups.com
You could have a look at AutoMapper http://www.codeplex.com/AutoMapper

Anthony Gatlin

unread,
Sep 15, 2009, 5:22:48 AM9/15/09
to Fluent NHibernate


On Sep 15, 2:23 am, anthony <anthony.tara...@gmail.com> wrote:

> This is in fact exactly what I'm doing now, but it feels somewhat
> extraneous.

If you have to manually extend every class that might be a bit of
work. One option is to reflect on the assembly and iterate through the
classes. (This is precisely what FNH does when it uses
AddFromAssembly). If you wanted an example, you could look at the FNH
code. I have not reviewed the FNH code, but the actual code is not
hard to write. Once you have reflected against the classes and
dynamically chosen the ones you want, you can generate new wrapper
classes programmatically. Then you can save these in an assembly and
reference it in your code. Any time the 3rd party assembly changes,
just re-rerun your code to build your wrappers.

> Clearly great minds think, and are named, alike.

I hope one day to be a great FNH mind.For now, I am just a
newbie. :)

> --anthony

Mikael Henriksson

unread,
Sep 15, 2009, 6:21:27 AM9/15/09
to fluent-n...@googlegroups.com
Ant that is part of what AutoMapper does...

anthony

unread,
Sep 15, 2009, 5:08:23 PM9/15/09
to Fluent NHibernate
Posting for posterity. I decided to go with a mix of fluent mappings
for classes that I define myself, and hbm.xml mappings for classes I
want to use as is.

For reference, here's what my configuration code looks like:

var config = Fluently.Configure().Database(...).Mappings(...);
config.ExposeConfiguration(c => c.AddAssembly(...));
...
config.BuildSessionFactory();

As for the hbm.xml, the key is to add the lazy=false attribute to your
<class> tag, and the access=field property to your <id> tag (and any
other <property> tags). For example:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Foo, Assembly.Of.Foo" lazy="false">
<id name="Id" type="Int32" access="field" />
</class>
</hibernate-mapping>

That should do it. This is the best solution for me. I'm looking
forward to the time when I can do everything fluently.

--anthony

On Sep 15, 3:21 am, Mikael Henriksson <mik...@zoolutions.se> wrote:
> Ant that is part of what AutoMapper does...
>
Reply all
Reply to author
Forward
0 new messages