From 770a8ec954330076923227267e899484215c3e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Thu, 16 Jul 2020 19:43:20 +0200 Subject: [PATCH 1/2] Improve slightly mapping documentation --- doc/reference/modules/basic_mapping.xml | 12 +- .../modules/nhibernate_mapping_attributes.xml | 216 +++++++++++------- doc/reference/modules/quickstart.xml | 4 + 3 files changed, 141 insertions(+), 91 deletions(-) diff --git a/doc/reference/modules/basic_mapping.xml b/doc/reference/modules/basic_mapping.xml index f30fa154507..950dae5de74 100644 --- a/doc/reference/modules/basic_mapping.xml +++ b/doc/reference/modules/basic_mapping.xml @@ -13,11 +13,13 @@ Note that, even though many NHibernate users choose to define XML mappings by hand, - a number of tools exist to generate the mapping document, including - NHibernate.Mapping.Attributes library and various template-based code - generators (CodeSmith, MyGeneration). You may also use - NHibernate.Mapping.ByCode available since NHibernate 3.2, or - Fluent NHibernate. + a number of tools exist to generate the mapping document, even transparently at runtime. + This includes the NHibernate.Mapping.Attributes + library which allows to directly annotate your entities with mapping declaration, + various template-based code generators (CodeSmith, MyGeneration), the built-in + NHibernate.Mapping.ByCode API available since NHibernate 3.2, or + the Fluent NHibernate + independent library. diff --git a/doc/reference/modules/nhibernate_mapping_attributes.xml b/doc/reference/modules/nhibernate_mapping_attributes.xml index baef69b80b3..042ca1e7084 100644 --- a/doc/reference/modules/nhibernate_mapping_attributes.xml +++ b/doc/reference/modules/nhibernate_mapping_attributes.xml @@ -2,59 +2,56 @@ NHibernate.Mapping.Attributes - What is NHibernate.Mapping.Attributes? - NHibernate.Mapping.Attributes is an add-in for <ulink url="http://nhibernate.info/">NHibernate</ulink> contributed by Pierre Henri Kuaté (aka <emphasis>KPixel</emphasis>); the former implementation was made by John Morris. + NHibernate.Mapping.Attributes is an add-in for <ulink url="http://nhibernate.info/">NHibernate</ulink> contributed by Pierre Henri KuatĂ© + (aka <emphasis>KPixel</emphasis>); the former implementation was made by John Morris. NHibernate require mapping streams to bind your domain model to your database. Usually, they are written (and maintained) in separated hbm.xml files. With NHibernate.Mapping.Attributes, you can use .NET attributes to decorate your entities and these attributes will be used to generate these mapping .hbm.xml (as files or streams). So you will no longer have to bother with these nasty xml files ;). - - Content of this library - - - - - NHibernate.Mapping.Attributes: That the only project you need (as end-user) - - - - Test: a working sample using attributes and HbmSerializer as NUnit TestFixture - - - - Generator: The program used to generate attributes and HbmWriter - - - - - Refly - : Thanks to Jonathan de Halleux for this library which make it so easy to generate code - - - - + + Content of this library project: + + + + + + NHibernate.Mapping.Attributes: That is the only assembly you need (as end-user). + + + + Test: a working sample using attributes and HbmSerializer for a NUnit TestFixture. + + + + Generator: the program used to generate the attributes and the HbmWriter of + the end-user assembly. + + + + + Refly + : thanks to Jonathan de Halleux for this library which make it so easy to generate code. + + + + This library is generated using the file /src/NHibernate.Mapping.Attributes/nhibernate-mapping.xsd (which is embedded in the assembly to be able to validate generated XML streams). - As this file can change at each new release of NHibernate, you should regenerate it before using it - with a different version (open the Generator solution, compile and run the Generator project). - But, no test has been done with versions prior to 0.8. + As this file can change at each new release of NHibernate, a new release of NHibernate.Mapping.Attributes + should be regenerated before using it with a different version. It can be done by opening the Generator solution, + compiling and running the Generator project. -
What's new? - - NHibernate - introduces many new features, improvements and changes: - It is possible to import classes by simply decorating them with [Import] class ImportedClass1 {}. Note that you must use HbmSerializer.Serialize(assembly); The <import/> mapping will be added before the classes mapping. If you prefer to keep these imports in the class using them, you can specify them all on the class: [Import(ClassType=typeof(ImportedClass1))] class Query {}. @@ -69,17 +66,27 @@ public class Base { [AttributeIdentifier(Name="Id.Column", Value="ID")] // Default value public int Id { ... } } + [AttributeIdentifier(Name="Id.Column", Value="SUB_ID")] -[Class] public class MappedSubClass : Base { } +[Class] +public class MappedSubClass : Base { } The idea is that, when you have a mapping which is shared by many subclasses but which has minor differences (like different column names), you can put the mapping in the base class with place holders on these fields and give their values in subclasses. Note that this is possible for any mapping field taking a string (column, name, type, access, etc.). And, instead of Value, you can use ValueType or ValueObject (if you use an enum, you can control its formatting with ValueObject). The "place holder" is defined like this: {{XXX}}. If you don't want to use these double curly brackets, you can change them using the properties StartQuote and EndQuote of the class HbmWriter. - It is possible to register patterns (using Regular Expressions) to automatically transform fully qualified names of properties types into something else. Eg: HbmSerializer.Default.HbmWriter.Patterns.Add(@"Namespace.(\S+), Assembly", "$1"); will map all properties with a not-qualified type name. + + It is possible to register patterns (using Regular Expressions) to automatically transform fully qualified names of properties types + into something else. Eg: HbmSerializer.Default.HbmWriter.Patterns.Add(@"Namespace\.(\S+), Assembly", "$1"); will + map all properties with a not-qualified type name. - Two methods have been added to allow writing: cfg.AddInputStream( HbmSerializer.Default.Serialize(typeof(XXX)) ) and cfg.AddInputStream( HbmSerializer.Default.Serialize(typeof(XXX).Assembly) ). So it is no longer required to create a MemoryStream for these simple cases. + + Two methods have been added to the HbmSerializer class, allowing generating mappings from a type or an assembly: + HbmSerializer.Default.Serialize(typeof(XXX)) and + HbmSerializer.Default.Serialize(typeof(XXX).Assembly). So it is no longer required to create a MemoryStream for + these simple cases. The output of these call can be directly added to your NHibernate Configuration instance: + cfg.AddInputStream(HbmSerializer.Default.Serialize(typeof(XXX))). Two WriteUserDefinedContent() methods have been added to HbmWriter. They improve the extensibility of this library; it is now very easy to create a .NET attribute and integrate it in the mapping. @@ -91,7 +98,11 @@ public class Base { Interfaces can be mapped (just like classes and structs). - A notable "bug" fix is the re-ordering of (joined-)subclasses; This operation may be required when a subclass extends another subclass. In this case, the extended class mapping must come before the extending class mapping. Note that the re-ordering takes place only for "top-level" classes (that is not nested in other mapped classes). Anyway, it is quite unusual to put a interdependent mapped subclasses in a mapped class. + + A notable "bug" fix is the re-ordering of (joined-)subclasses; This operation may be required when a subclass extends another subclass. + In this case, the extended class mapping must come before the extending class mapping. Note that the re-ordering takes place only for + "top-level" classes (that is not nested in other mapped classes). Anyway, it is quite unusual to put an interdependent mapped subclasses + in a mapped class. There are also many other little changes; refer to the release notes for more details. @@ -99,49 +110,64 @@ public class Base {
- -
How to use it? The <emphasis>end-user class</emphasis> is <classname>NHibernate.Mapping.Attributes.HbmSerializer</classname> - This class serialize your domain model to mapping streams. You can either serialize classes one by one or an assembly. Look at NHibernate.Mapping.Attributes.Test project for a working sample. + + This class serialize your domain model to mapping streams. You can either serialize classes one by one, + or serialize a whole assembly. Look at NHibernate.Mapping.Attributes.Test project for a working sample. + - The first step is to decorate your entities with attributes; you can use: [Class], [Subclass], [JoinedSubclass] or [Component]. Then, you decorate your members (fields/properties); they can take as many attributes as required by your mapping. Eg: + + The first step is to decorate your entities with attributes. You can use: [Class], + [Subclass], [JoinedSubclass] or [Component]. Then, you decorate + your members (fields/properties); they can take as many attributes as required by your mapping. Eg: + - [NHibernate.Mapping.Attributes.Class] - public class Example - { - [NHibernate.Mapping.Attributes.Property] - public string Name; - } - After this step, you use NHibernate.Mapping.Attributes.HbmSerializer: (here, we use Default which is an instance you can use if you don't need/want to create it yourself). - NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration(); - cfg.Configure(); - // Enable validation (optional) - NHibernate.Mapping.Attributes.HbmSerializer.Default.Validate = true; - // Here, we serialize all decorated classes (but you can also do it class by class) - cfg.AddInputStream( NHibernate.Mapping.Attributes.HbmSerializer.Default.Serialize( - System.Reflection.Assembly.GetExecutingAssembly() ); ); - // Now you can use this configuration to build your SessionFactory... +[NHibernate.Mapping.Attributes.Class] +public class Example +{ + [NHibernate.Mapping.Attributes.Property] + public string Name; +} + + After this step, you use NHibernate.Mapping.Attributes.HbmSerializer (here, we use its + Default property, which is an instance you can use if you don't need/want to create it yourself): + + var cfg = new NHibernate.Cfg.Configuration(); +cfg.Configure(); +// Enable validation (optional) +HbmSerializer.Default.Validate = true; +// Here, we serialize all decorated classes (but you can also do it class by class) +cfg.AddInputStream(HbmSerializer.Default.Serialize( + System.Reflection.Assembly.GetExecutingAssembly())); +// Now you can use this configuration to build your SessionFactory... - As you can see here: NHibernate.Mapping.Attributes is not (really) intrusive. + As you can see here, NHibernate.Mapping.Attributes is not (really) intrusive. Setting attributes on your objects doesn't force you to use them with NHibernate and doesn't break any constraint on your architecture. Attributes are purely informative (like documentation)!
-
Tips - In production, it is recommended to generate a XML mapping file from NHibernate.Mapping.Attributes and use this file each time the SessionFactory need to be built. Use: HbmSerializer.Default.Serialize(typeof(XXX).Assembly, "DomainModel.hbm.xml"); It is slightly faster. + + In production, you may want to generate a XML mapping file from NHibernate.Mapping.Attributes and use this file each time + the SessionFactory need to be built. Use: HbmSerializer.Default.Serialize(typeof(XXX).Assembly, "DomainModel.hbm.xml");. + It is slightly faster. + - Use HbmSerializer.Validate to enable/disable the validation of generated xml streams (against NHibernate mapping schema); this is useful to quickly find errors (they are written in StringBuilder HbmSerializer.Error). If the error is due to this library then see if it is a know issue and report it; you can contribute a solution if you solve the problem :) + + Use HbmSerializer.Validate to enable/disable the validation of generated xml streams (against + NHibernate mapping schema). This is useful to quickly find errors. (They are written in the StringBuilder property + HbmSerializer.Error.) If the error is due to this library, then see if it is a know issue and report it. + You are welcome to contribute a solution if you solve the problem :). Your classes, fields and properties (members) can be private; just make sure that you have the permission to access private members using reflection (ReflectionPermissionFlag.MemberAccess). @@ -156,7 +182,12 @@ public class Base { By default, .NET attributes don't keep the order of attributes; so you need to set it yourself when the order matter (using the first parameter of each attribute); it is highly recommended to set it when you have more than one attribute on the same member. - As long as there is no ambiguity, you can decorate a member with many unrelated attributes. A good example is to put class-related attributes (like <discriminator>) on the identifier member. But don't forget that the order matters (the <discriminator> must be after the <id>). The order used comes from the order of elements in the NHibernate mapping schema. Personally, I prefer using negative numbers for these attributes (if they come before!). + + As long as there is no ambiguity, you can decorate a member with many unrelated attributes. A good example is to put + class-related attributes (like <discriminator>) on the identifier member. But don't forget + that the order matters (the <discriminator> must be after the <id>). + The order to use comes from the order of elements in the NHibernate mapping schema. Personally, I prefer using negative + numbers for these attributes (if they come first!). You can add [HibernateMapping] on your classes to specify <hibernate-mapping> attributes (used when serializing the class in its stream). You can also use HbmSerializer.Hbm* properties (used when serializing an assembly or a type that is not decorated with [HibernateMapping]). @@ -188,24 +219,23 @@ public class Base { Use the property HbmSerializer.HbmWriter to change the writer used (you may set a subclass of HbmWriter). - Example using some this tips: (0, 1 and 2 are position indexes) + Example using some of these tips: (0, 1 and 2 are position indexes) // Don't put it after [ManyToOne] !!! - [NHibernate.Mapping.Attributes.Id(0, TypeType=typeof(int))] - [NHibernate.Mapping.Attributes.Generator(1, Class="uuid.hex")] - [NHibernate.Mapping.Attributes.ManyToOne(2, - ClassType=typeof(Foo), OuterJoin=OuterJoinStrategy.True)] - private Foo Entity; +[NHibernate.Mapping.Attributes.Id(0, TypeType=typeof(int))] + [NHibernate.Mapping.Attributes.Generator(1, Class="uuid.hex")] +[NHibernate.Mapping.Attributes.ManyToOne(2, + ClassType=typeof(Foo), OuterJoin=OuterJoinStrategy.True)] +private Foo Entity; Generates: - - - + + + + ]]>
-
Known issues and TODOs First, read TODOs in the source code ;) @@ -213,17 +243,17 @@ public class Base { A Position property has been added to all attributes to order them. But there is still a problem: When a parent element "p" has a child element "x" that is also the child element of another child element "c" of "p" (preceding "x") :D Illustration: - - - + + +

]]>
In this case, when writing: [Attributes.P(0)] - [Attributes.C(1)] - [Attributes.X(2)] - [Attributes.X(3)] +[Attributes.C(1)] + [Attributes.X(2)] +[Attributes.X(3)] public MyType MyProperty; X(3) will always belong to C(1) ! (as X(2)). @@ -233,7 +263,7 @@ public MyType MyProperty; For now, you can fallback on [RawXml]. Actually, there is no other know issue nor planned modification. This library should be stable and complete; but if you find a bug or think of an useful improvement, contact us! - On side note, it would be nice to write a better TestFixture than NHibernate.Mapping.Attributes.Test :D + As a side note, it would be nice to write a better TestFixture than NHibernate.Mapping.Attributes.Test :D.
@@ -242,19 +272,33 @@ public MyType MyProperty; Any change to the schema (nhibernate-mapping.xsd) implies: - Checking if there is any change to do in the Generator (like updating KnowEnums / AllowMultipleValue / IsRoot / IsSystemType / IsSystemEnum / CanContainItself) + + Checking if there is any change to do in the Generator (like updating KnowEnums / + AllowMultipleValue / IsRoot / IsSystemType / + IsSystemEnum / CanContainItself). + - Updating /src/NHibernate.Mapping.Attributes/nhibernate-mapping.xsd (copy/paste) and running the Generator again (even if it wasn't modified) + + Updating /src/NHibernate.Mapping.Attributes/nhibernate-mapping.xsd (copy/paste) + and running the Generator again (even if it wasn't modified). + - Running the Test project and make sure that no exception is thrown. A class/property should be modified/added in this project to be sure that any new breaking change will be caught (=> update the reference hbm.xml files and/or the project NHibernate.Mapping.Attributes.csproj) + + Running the Test project and make sure that no exception is thrown. A class/property should be modified/added + in this project to be sure that any new breaking change will be caught (=> update the reference hbm.xml files + and/or the project NHibernate.Mapping.Attributes.csproj). + This implementation is based on NHibernate mapping schema; so there is probably lot of "standard schema features" that are not supported... The version of NHibernate.Mapping.Attributes should be the version of the NHibernate schema used to generate it (=> the version of NHibernate library). - In the design of this project, performance is a (very) minor goal :) Easier implementation and maintenance are far more important because you can (and should) avoid to use this library in production (Cf. the first tip in ). + + In the design of this project, performance is a (very) minor goal :). Easier implementation and maintenance + are far more important because you can use this library to generate statically the mapping files and use them instead in production. + (Cf. the first tip in .) + -
diff --git a/doc/reference/modules/quickstart.xml b/doc/reference/modules/quickstart.xml index 1185ae2cdfa..da6d4e6c877 100644 --- a/doc/reference/modules/quickstart.xml +++ b/doc/reference/modules/quickstart.xml @@ -154,6 +154,10 @@ Mapping the cat + + This tutorial directly uses xml mapping files. This is just one among many mapping solution + NHibernate provides, see . + The Cat.hbm.xml mapping file contains the metadata required for the object/relational mapping. The metadata includes declaration From cd8e695aa8860bf8d9afe8b578f8c91355d99233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Fri, 17 Jul 2020 12:18:42 +0200 Subject: [PATCH 2/2] Fix some more typo --- doc/reference/modules/basic_mapping.xml | 2 +- .../modules/nhibernate_mapping_attributes.xml | 25 +++++++++++-------- doc/reference/modules/quickstart.xml | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/doc/reference/modules/basic_mapping.xml b/doc/reference/modules/basic_mapping.xml index 950dae5de74..30166e03d4a 100644 --- a/doc/reference/modules/basic_mapping.xml +++ b/doc/reference/modules/basic_mapping.xml @@ -15,7 +15,7 @@ Note that, even though many NHibernate users choose to define XML mappings by hand, a number of tools exist to generate the mapping document, even transparently at runtime. This includes the NHibernate.Mapping.Attributes - library which allows to directly annotate your entities with mapping declaration, + library which allows to directly annotate your entities with mapping declarations, various template-based code generators (CodeSmith, MyGeneration), the built-in NHibernate.Mapping.ByCode API available since NHibernate 3.2, or the Fluent NHibernate diff --git a/doc/reference/modules/nhibernate_mapping_attributes.xml b/doc/reference/modules/nhibernate_mapping_attributes.xml index 042ca1e7084..cdb4c7c8764 100644 --- a/doc/reference/modules/nhibernate_mapping_attributes.xml +++ b/doc/reference/modules/nhibernate_mapping_attributes.xml @@ -17,7 +17,7 @@ - NHibernate.Mapping.Attributes: That is the only assembly you need (as end-user). + NHibernate.Mapping.Attributes: that is the only assembly you need (as an end-user). @@ -32,7 +32,7 @@ Refly - : thanks to Jonathan de Halleux for this library which make it so easy to generate code. + : thanks to Jonathan de Halleux for this library which makes it so easy to generate code.
@@ -78,7 +78,8 @@ public class MappedSubClass : Base { } It is possible to register patterns (using Regular Expressions) to automatically transform fully qualified names of properties types into something else. Eg: HbmSerializer.Default.HbmWriter.Patterns.Add(@"Namespace\.(\S+), Assembly", "$1"); will - map all properties with a not-qualified type name. + map all properties with a not-qualified type name. + @@ -86,7 +87,8 @@ public class MappedSubClass : Base { } HbmSerializer.Default.Serialize(typeof(XXX)) and HbmSerializer.Default.Serialize(typeof(XXX).Assembly). So it is no longer required to create a MemoryStream for these simple cases. The output of these call can be directly added to your NHibernate Configuration instance: - cfg.AddInputStream(HbmSerializer.Default.Serialize(typeof(XXX))). + cfg.AddInputStream(HbmSerializer.Default.Serialize(typeof(XXX))). + Two WriteUserDefinedContent() methods have been added to HbmWriter. They improve the extensibility of this library; it is now very easy to create a .NET attribute and integrate it in the mapping. @@ -99,13 +101,14 @@ public class MappedSubClass : Base { } - A notable "bug" fix is the re-ordering of (joined-)subclasses; This operation may be required when a subclass extends another subclass. + A notable "bug" fix is the re-ordering of (joined-)subclasses. This operation may be required when a subclass extends another subclass. In this case, the extended class mapping must come before the extending class mapping. Note that the re-ordering takes place only for "top-level" classes (that is not nested in other mapped classes). Anyway, it is quite unusual to put an interdependent mapped subclasses - in a mapped class. + in a mapped class. + - There are also many other little changes; refer to the release notes for more details. + There are also many other little changes: refer to the release notes for more details. @@ -166,8 +169,8 @@ cfg.AddInputStream(HbmSerializer.Default.Serialize( Use HbmSerializer.Validate to enable/disable the validation of generated xml streams (against NHibernate mapping schema). This is useful to quickly find errors. (They are written in the StringBuilder property - HbmSerializer.Error.) If the error is due to this library, then see if it is a know issue and report it. - You are welcome to contribute a solution if you solve the problem :). + HbmSerializer.Error.) If the error is due to this library, then see if it is a known issue and report it. + You are welcome to contribute a solution if you solve the trouble :). Your classes, fields and properties (members) can be private; just make sure that you have the permission to access private members using reflection (ReflectionPermissionFlag.MemberAccess). @@ -262,7 +265,7 @@ public MyType MyProperty; Anyway, the solution would be to add a int ParentNode property to BaseAttribute so that you can create a real graph... For now, you can fallback on [RawXml]. - Actually, there is no other know issue nor planned modification. This library should be stable and complete; but if you find a bug or think of an useful improvement, contact us! + Actually, there is no other know issue nor planned modification. This library should be stable and complete. But if you find a bug or think of an useful improvement, contact us! As a side note, it would be nice to write a better TestFixture than NHibernate.Mapping.Attributes.Test :D. @@ -292,7 +295,7 @@ public MyType MyProperty; - This implementation is based on NHibernate mapping schema; so there is probably lot of "standard schema features" that are not supported... + This implementation is based on NHibernate mapping schema. So there is probably lot of "standard schema features" that are not supported... The version of NHibernate.Mapping.Attributes should be the version of the NHibernate schema used to generate it (=> the version of NHibernate library). In the design of this project, performance is a (very) minor goal :). Easier implementation and maintenance diff --git a/doc/reference/modules/quickstart.xml b/doc/reference/modules/quickstart.xml index da6d4e6c877..f949f5c422e 100644 --- a/doc/reference/modules/quickstart.xml +++ b/doc/reference/modules/quickstart.xml @@ -155,7 +155,7 @@ Mapping the cat - This tutorial directly uses xml mapping files. This is just one among many mapping solution + This tutorial directly uses xml mapping files. This is just one among many mapping solutions NHibernate provides, see .