Obtener nombres de campo de clase y nombres de columna de tabla de metadatos de NHibernate

StackOverflow https://stackoverflow.com/questions/1800930

  •  05-07-2019
  •  | 
  •  

Pregunta

Trasfondo

Estoy usando una base de datos heredada con todo tipo de rincones feos. Un bit es la auditoría. Hay una tabla que enumera las combinaciones de campos de nombre de tabla / campo que deberían tener una pista de auditoría. Por ejemplo, si hay una fila que tiene " WORKORDER " para el nombre de la tabla y "ESTADO" para el nombre del campo, luego necesito agregar filas a la tabla de auditoría cada vez que la propiedad Workorder.Status cambia en la aplicación. Conozco el enfoque: eventos NH o interceptores, pero tengo un problema que resolver antes de llegar a esa etapa.

Pregunta

Lo que necesito saber es cómo obtener una lista de pares clave / valor para una sola clase persistente que contiene (a) el nombre del campo de la base de datos y (b) el nombre de la propiedad asociada en la clase. Entonces, para mi ejemplo, tengo una clase llamada Workorder asociada con una tabla llamada (no es de extrañar) WORKORDER. Tengo una propiedad en esa clase Workorder llamada CurrentStatus. La propiedad coincidente en la tabla WORKORDER es STATUS. ¿Observa la falta de coincidencia entre el nombre de la propiedad y el nombre de la columna de la tabla? Necesito saber el nombre de la propiedad para acceder a los datos de antes y después de la auditoría. Pero también necesito saber el nombre de la columna de respaldo para poder consultar el estúpido legado '' AuditTheseColumns '' mesa.

Lo que he probado

en mi aplicación, cambio el Workorder.CurrentStatus de '' TS '' a "IP". Miro en mi tabla de seguimiento de auditoría y veo que se rastrea la columna WORKORDER.STATUS. Entonces, después de llamar a Session.SaveOrUpdate (orden de trabajo), necesito encontrar la propiedad Workorder asociada con la columna ESTADO y hacer una sesión. .

Por lo que puedo decir, puede obtener información sobre la clase:

        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++;
        }

E información sobre la tabla:

        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);
            }
        }

Pero no al mismo tiempo. ¿Algunas ideas? No sé dónde mirar a continuación.

¿Fue útil?

Solución

Ahora, si entiendo correctamente, esto es lo que podrías hacer ...

Una forma sería leer y analizar los archivos de mapeo XML del dll que están incrustados antes o incluso después de que se construye la fábrica de sesiones NHibernate. De esta manera, puede obtener toda la información que necesita de los archivos XML (con la columna correspondiente a cada propiedad) y completar una colección global (probablemente estática) de objetos personalizados que contendrán el nombre de la entidad y un diccionario con clave, el nombre y el valor correctos. el nombre de la columna (o al revés).

Puede acceder a esta colección global para obtener la información que necesita justo después de la llamada a SaveOrUpdate () como la describió. La desventaja de este enfoque es que necesita escribir su propia lógica de análisis XML para recuperar la información que necesita de los archivos de mapeo XML.

Una alternativa sería crear un atributo personalizado para decorar cada propiedad de sus entidades para obtener el nombre de columna que corresponde a cada propiedad. Un ejemplo sería:

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

Usando la reflexión puede obtener fácilmente el nombre de la propiedad y del atributo el nombre de la columna a la que está asignada esta propiedad.

La desventaja de este enfoque sería tener que sincronizar los nombres de sus columnas con los valores de los atributos cuando se actualiza el esquema de la base de datos.

Otros consejos

Cómo obtener los nombres de columna / campo de la base de datos y los nombres de propiedad de clase para una entidad asignada por 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's NHibernate metadata
            var metaData = sessionFactory.GetClassMetadata(type.ToString());

            // Gets the entity's 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's 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;
        }
    }
}

Uso:

// 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);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top