Obtendo nomes de campo de classe e nomes de coluna da tabela de NHibernate metadados

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

  •  05-07-2019
  •  | 
  •  

Pergunta

Fundo

Eu estou usando um banco de dados legado com todos os tipos de cantos feias. Um bit é a auditoria. Há uma tabela listando as combinações tablename / campo de campos que devem ter uma trilha de auditoria. Por exemplo, se há uma linha que tem "WorkOrder" para o nome da tabela e "STATUS" para o nome do campo, então eu preciso adicionar linha (s) para a tabela de auditoria sempre que os Workorder.Status alterações de propriedade no aplicativo. Eu sei que a abordagem: eventos NH ou interceptadores, mas eu tenho um problema de descobrir antes de eu chegar a essa fase

.

Pergunta

O que eu preciso saber é como obter uma lista de pares de chave / valor para um único classe persistente que contém (a) o nome do campo de banco de dados e (b) o nome da propriedade associada da classe. Então para o meu exemplo, eu tenho uma classe chamada Ordem de Trabalho associado com uma tabela chamada (nenhuma surpresa) WorkOrder. Eu tenho uma propriedade em que a classe Ordem de Trabalho chamado CurrentStatus. A propriedade correspondente na tabela WorkOrder é STATUS. Observe a incompatibilidade entre o nome da propriedade eo nome da coluna da tabela? Eu preciso saber o nome da propriedade para acessar o antes e depois de dados para a auditoria. Mas eu também preciso saber o nome da coluna de apoio para que eu possa consultar o legado estúpido "AuditTheseColumns" mesa.

O que eu tentei

no meu aplicativo eu mudar o Workorder.CurrentStatus de "TS" para "IP". Eu olho em minha mesa de rastreamento de auditoria e ver que a coluna WORKORDER.STATUS é monitorado. Então, depois de chamar Session.saveOrUpdate (workorder), eu preciso encontrar a propriedade Ordem de Trabalho associado à coluna STATUS e fazer um Session.Save (auditRecord) dizendo que a velha valores novos ( "IP") ( "TS") e.

Tanto quanto eu posso dizer, você pode obter informações sobre a classe:

        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 informações sobre a tabela:

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

Mas não ao mesmo tempo. Alguma ideia? Eu estou em uma perda para onde olhar em seguida.

Foi útil?

Solução

Agora, se eu entendi corretamente aqui é o que você poderia fazer ....

Uma forma seria a de ler e analisar os arquivos de mapeamento XML do dll que são incorporados antes ou mesmo após a fábrica de sessão NHibernate é de construção. Desta forma, você pode obter todas as informações que você precisa a partir dos arquivos XML (com corresponde coluna para que a propriedade) e preencher uma coleção global (provavelmente estática) de objetos personalizados que irá manter o nome da entidade e um dicionário com chave o nome propery e valor o nome da coluna (ou o outro em torno de forma).

Você pode acessar esta coleção global para obter a informação que você precisa logo após a chamada para saveOrUpdate (), como você descreveu. A desvantagem dessa abordagem é que você precisa para escrever sua própria lógica de análise XML retrive a informação que você precisa a partir dos arquivos de mapeamento XML.

Uma alternativa seria criar um atributo personalizado para decorar cada propriedade de suas entidades a fim de obter o nome da coluna que corresponde a cada propriedade. Um exemplo seria:

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

Usando reflexão você pode facilmente obter o nome da propriedade ea partir do atributo o nome da coluna que esta propriedade é mapeada para.

A desvantagem dessa abordagem seria ter que manter em sincronia os nomes das colunas com os valores de atributo quando o esquema de banco de dados é atualizado.

Outras dicas

Como chegar a coluna de banco de dados nomes / campo e nomes de propriedade de classe para uma entidade mapeada pelo 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top