Retrieving class field names and table column names from NHibernate metadata

Background

I use an outdated database with all kinds of ugly corners. One bit is auditing. There is a table that lists combinations of tablename / field fields that should have an audit trail. For example, if there is a row that has “WORKORDER” for the table name and “STATUS” for the field name, then I need to add rows (rows) to the audit table whenever the Workorder.Status property changes in the application. I know the approach: NH events or interceptors, but I have a problem to figure out before I get to this stage.

Question

I need to know how to get a list of key / value pairs for one constant class that contains (a) the name of the database field and (b) the name of the property associated with it in the class. So for my example, I have a class called Workorder that is associated with the (not surprisingly) WORKORDER table. I have a property in this Workorder class called CurrentStatus. The matching property in the WORKORDER table is STATUS. Notice the mismatch between the property name and the column name of the table? I need to know the name of the property to access data before and after the audit. But I also need to know the name of the support column so that I can query the dumb table "Audit TheseColumns".

What i tried

in my application, I change Workorder.CurrentStatus from "TS" to "IP". I look at the audit tracking table and see that the WORKORDER.STATUS column is being tracked. Therefore, after calling Session.SaveOrUpdate (workorder), I need to find the Workorder property associated with the STATUS column and execute Session.Save (auditRecord), telling it the old ("TS") and new ("IP") values.

As far as I can tell, you can get class information:

var fieldNames = new List<string>(); IClassMetadata classMetadata = SessionFactory(Resources.CityworksDatasource).GetClassMetadata(typeof(T)); int propertyCount = 0; foreach (IType propertyType in classMetadata.PropertyTypes) { if (propertyType.IsComponentType) { var cp = (ComponentType)propertyType; foreach (string propertyName in cp.PropertyNames) { fieldNames.Add(propertyName); } } else if(!propertyType.IsCollectionType) { fieldNames.Add(classMetadata.PropertyNames[propertyCount + 1]); } propertyCount++; } 

And table information:

  var columnNames = new List<string>(); PersistentClass mappingMeta = ConfigureCityworks().GetClassMapping(typeof(T)); foreach (Property property in mappingMeta.PropertyIterator) { foreach (Column selectable in property.ColumnIterator) { if (columnNames.Contains(selectable.Name)) continue; columnNames.Add(selectable.Name); } } 

But not at the same time. Any ideas? I’m at a loss where to look further.

+6
nhibernate metadata
source share
2 answers

Now, if I understand correctly, here is what you could do ....

One way is to read and parse XML mapping files from DLLs that are embedded before or even after the NHibernate factory is built. Thus, you can get all the necessary information from XML files (with a column that corresponds to which property) and fill out a global (possibly static) collection of user objects that will contain the entity name and a dictionary with the key name and value column name (or vice versa).

You can then access this global collection to get the information you need right after calling SaveOrUpdate (), as you described it. The disadvantage of this approach is that you need to write your own XML parsing logic to extract the necessary information from the XML mapping files.

An alternative would be to create a custom attribute to decorate each property of your objects to get a column name corresponding to each property. An example is:

 [ColumnName("MyColumn")] public string Status { get; set; } 

Using reflection, you can easily get the property name and attribute from the name of the column on which this property is mapped.

The disadvantage of this approach would be to synchronize the column names with the attribute values ​​when updating the database schema.

+1
source share

How to get database column names / field names and class property names for an object displayed by NHibernate:

 using System; using System.Collections.Generic; using System.Reflection; using NHibernate; using NHibernate.Persister.Entity; namespace Stackoverflow.Example { /// <summary> /// NHibernate helper class /// </summary> /// <remarks> /// Assumes you are using NHibernate version 3.1.0.4000 or greater (Not tested on previous versions) /// </remarks> public class NHibernateHelper { /// <summary> /// Creates a dictionary of property and database column/field name given an /// NHibernate mapped entity /// </summary> /// <remarks> /// This method uses reflection to obtain an NHibernate internal private dictionary. /// This is the easiest method I know that will also work with entitys that have mapped components. /// </remarks> /// <param name="sessionFactory">NHibernate SessionFactory</param> /// <param name="entity">An mapped entity</param> /// <returns>Entity Property/Database column dictionary</returns> public static Dictionary<string, string> GetPropertyAndColumnNames(ISessionFactory sessionFactory, object entity) { // Get the objects type Type type = entity.GetType(); // Get the entity NHibernate metadata var metaData = sessionFactory.GetClassMetadata(type.ToString()); // Gets the entity persister var persister = (AbstractEntityPersister)metaData; // Creating our own Dictionary<Entity property name, Database column/filed name>() var d = new Dictionary<string, string>(); // Get the entity identifier string entityIdentifier = metaData.IdentifierPropertyName; // Get the database identifier // Note: We are only getting the first key column. // Adjust this code to your needs if you are using composite keys! string databaseIdentifier = persister.KeyColumnNames[0]; // Adding the identifier as the first entry d.Add(entityIdentifier, databaseIdentifier); // Using reflection to get a private field on the AbstractEntityPersister class var fieldInfo = typeof(AbstractEntityPersister) .GetField("subclassPropertyColumnNames", BindingFlags.NonPublic | BindingFlags.Instance); // This internal NHibernate dictionary contains the entity property name as a key and // database column/field name as the value var pairs = (Dictionary<string, string[]>)fieldInfo.GetValue(persister); foreach (var pair in pairs) { if (pair.Value.Length > 0) { // The database identifier typically appears more than once in the NHibernate dictionary // so we are just filtering it out since we have already added it to our own dictionary if (pair.Value[0] == databaseIdentifier) break; d.Add(pair.Key, pair.Value[0]); } } return d; } } } 

Using:

 // Get your NHiberate SessionFactory wherever that is in your application var sessionFactory = NHibernateHelper.SessionFactory; // Get an entity that you know is mapped by NHibernate var customer = new Customer(); // Get a dictionary of the database column / field names and their corresponding entity property names var propertyAndColumnNamesDictionary = Stackoverflow.Example.NHibernateHelper.GetPropertyAndColumnNames(sessionFactory, customer); 
+11
source share

All Articles