Получение имен полей класса и имен столбцов таблицы из метаданных NHibernate - PullRequest
6 голосов
/ 26 ноября 2009

Фон

Я использую устаревшую базу данных со всеми видами уродливых углов. Один бит - одитинг. Существует таблица, в которой перечислены комбинации таблиц и имен полей, которые должны иметь контрольный журнал. Например, если есть строка с именем «WORKORDER» для имени таблицы и «STATUS» для имени поля, то мне нужно добавлять строки в таблицу аудита всякий раз, когда свойство Workorder.Status изменяется в приложении. Я знаю подход: события NH или перехватчики, но у меня есть проблема, которую нужно выяснить, прежде чем я доберусь до этой стадии.

Вопрос

Что мне нужно знать, так это как получить список пар ключ / значение для одного постоянного класса, содержащего (а) имя поля базы данных и (б) имя связанного свойства в классе. Так что для моего примера у меня есть класс Workorder, связанный с таблицей (не удивительно) WORKORDER. У меня есть свойство в этом классе Workorder под названием CurrentStatus. Соответствующим свойством в таблице WORKORDER является STATUS. Заметили несоответствие между именем свойства и именем столбца таблицы? Мне нужно знать имя свойства для доступа к данным до и после аудита. Но мне также нужно знать имя вспомогательного столбца, чтобы я мог запросить глупую устаревшую таблицу AuditTheseColumns.

Что я пробовал

в своем приложении я изменяю Workorder.CurrentStatus с "TS" на "IP". Я просматриваю свою таблицу отслеживания аудита и вижу, что столбец WORKORDER.STATUS отслеживается. Поэтому после вызова Session.SaveOrUpdate (workorder) мне нужно найти свойство Workorder, связанное со столбцом STATUS, и выполнить Session.Save (auditRecord), сообщив ему старые ("TS") и новые ("IP") значения.

Насколько я могу судить, вы можете получить информацию о классе:

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

и информация о таблице:

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

Но не одновременно. Есть идеи? Я в недоумении, где искать дальше.

Ответы [ 2 ]

12 голосов
/ 08 декабря 2011

Как получить имена столбцов / полей базы данных и имена свойств класса для сущности, сопоставленной с помощью 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;
        }
    }
}

Использование:

// 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);
1 голос
/ 26 ноября 2009

Теперь, если я правильно понимаю, вот что вы могли бы сделать ...

Одним из способов будет чтение и анализ файлов сопоставления XML из библиотеки DLL, которые встроены до или даже после сборки фабрики сеансов NHibernate. Таким образом, вы можете получить всю необходимую вам информацию из файлов XML (столбец которой соответствует какому свойству) и заполнить глобальную (возможно, статическую) коллекцию пользовательских объектов, в которой будут храниться имя сущности и словарь с ключом, именем и значением свойства. имя столбца (или наоборот).

Затем вы можете получить доступ к этой глобальной коллекции, чтобы получить необходимую информацию сразу после вызова SaveOrUpdate (), как вы ее описали. Недостатком этого подхода является то, что вам нужно написать собственную логику синтаксического анализа XML, чтобы извлечь нужную информацию из файлов сопоставления XML.

Альтернативой может быть создание настраиваемого атрибута для украшения каждого свойства ваших объектов, чтобы получить имя столбца, соответствующее каждому свойству. Примером может быть:

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

Используя отражение, вы можете легко получить имя свойства и из атрибута имя столбца, которому сопоставлено это свойство.

Недостатком этого подхода является необходимость синхронизации имен столбцов со значениями атрибутов при обновлении схемы базы данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...