[ELMA3] Архитектура объектной модели
Общие принципы
Объектная модель системы – набор классов, сущностей, перечислений, которые используются в предметной области. Под сущностью в системе ELMA будем понимать объект, способный храниться в базе данных. Основные классы для работы с объектной моделью представлены в сборке EleWise.ELMA.SDK в пространствах имен, начинающихся с EleWise.ELMA.Model.
Работа с объектной моделью системы ELMA построена на следующих принципах:
Метаданные
Описание объектной модели в системе ELMA основано на использовании метаданных.
Метаданные – данные, описывающие некоторый элемент объектной модели (например, тип сущности, перечисление и т.д.).
Для создания метаданных (создания типов сущностей, перечислений) используется специальный плагин к Visual Studio, позволяющий визуально описывать модель системы (см. статью Редактор сущностей). Основные типы метаданных представлены в пространстве имен EleWise.ELMA.Model.Metadata. В файловой системе метаданные хранятся в файлах формата .md (в действительности - обычный xml-файл).
Пример метаданных сущности:
<?xml version="1.0" encoding="utf-8"?> <Entity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" IsUnique="true"> <Uid>04d5bcbf-4288-4d95-9745-1ebd0d690bb3</Uid> <Name>User</Name> <DisplayName>Пользователь</DisplayName> <Namespace>EleWise.ELMA.Model.Entities.Security</Namespace> <Properties> <PropertyMetadata xsi:type="EntityPropertyMetadata"> <Uid>9357aacc-d37f-497a-99f4-4a106765f8e1</Uid> <Name>UserName</Name> <DisplayName>Учетная запись</DisplayName> <TypeUid>9b9aac17-22bb-425c-aa93-9c02c5146965</TypeUid> <Settings xsi:type="StringSettings"> <FieldName>UserName</FieldName> </Settings> <Required>true</Required> <Filterable>true</Filterable> </PropertyMetadata> <PropertyMetadata xsi:type="EntityPropertyMetadata"> <Uid>a1e9ab10-6dad-4a11-a190-8f9d11b9ed77</Uid> <Name>Password</Name> <DisplayName>Пароль</DisplayName> <TypeUid>9b9aac17-22bb-425c-aa93-9c02c5146965</TypeUid> <Settings xsi:type="StringSettings"> <FieldName>Password</FieldName> </Settings> </PropertyMetadata> </Properties> <Type>Interface</Type> <ImplementationUid>18faf3ae-03c9-4e64-b02a-95dd63e54c4d</ImplementationUid> <TableName>User</TableName> <Filterable>true</Filterable> </Entity>
На основе метаданных автоматически генерируется исходный код на C# с типами, которые определяют метаданные. В данном случае будет автоматически сгенерирован следующий код:
namespace EleWise.ELMA.Model.Entities.Security { /// <summary> /// Пользователь /// </summary> [MetadataType(typeof(EntityMetadata))] [EntityMetadataType(EntityMetadataType.Interface)] [Uid("04d5bcbf-4288-4d95-9745-1ebd0d690bb3")] [ImplementationUid("18faf3ae-03c9-4e64-b02a-95dd63e54c4d")] public partial interface IUser : IEntity<long> { /// <summary> /// Учетная запись /// </summary> string UserName { get; set; } /// <summary> /// Пароль /// </summary> string Password { get; set; } } }
NHibernate
Для работы с базой данных в ELMA используется ORM-компонент NHibernate.Для упрощения работы с NHibernate в SDK ELMA реализован следующий функционал:
- маппинги сущностей создаются автоматически при создании через расширение для Visual Studio;
- работа с сессиями ведется в менеджерах сущностей;
- для менеджеров сущностей существует базовый класс, реализующий основные методы для работы с ними (Load, Save, Find и т.д.);
- транзакционность обеспечивается навешиванием атрибута TransactionAttribute на методы менеджера.
Менеджеры
Менеджер – некий сервис, предназначенный для работы с одним или несколькими элементами объектной модели. Обычно менеджеры создаются для работы с сущностями (см. статью Менеджеры сущностей).
Расширяемость объектной модели
Расширяемость объектной модели – принцип, позволяющий модулям системы создавать свои модели, а также расширять существующие (например, добавлять свойства в сущности из другого модуля. Данный принцип основан на использовании интерфейсов сущностей и интерфейсов расширения сущностей (см. статью Сущности).
Модули с объектными моделями
Элементы объектной модели (сущности, перечисления) можно создавать в любой сборке при наличии у нее атрибута EleWise.ELMA.Model.Attributes.ModelAssemblyAttribute.
Добавить его можно двумя способами:
1. Вручную – прописав в Properties\AssemblyInfo.cs следующую строчку:
[assembly: EleWise.ELMA.Model.Attributes.ModelAssemblyAttribute]
2. Используя расширение для Visual Studio, добавив элемент с типом ELMA Assembly Info и поставив галочку Сборка содержит модели.
Сущности
См. статью Сущности.
Работа с метаданными
Для работы с метаданными существует служба EleWise.ELMA.Model.Services.IMetadataRuntimeService.
namespace EleWise.ELMA.Model.Services { /// <summary> /// Интерфейс сервиса для получения информации о метаданных /// </summary> [ExtensionPoint] public interface IMetadataRuntimeService { /// <summary> /// Получить тип по уникальному идентификатору /// </summary> /// <param name="uid">Уникальный идентификатор типа</param> /// <param name="loadImplementation">Загружать ли метаданные реализации, если указанный тип - интерфейс</param> /// <returns>Тип</returns> Type GetTypeByUid(Guid uid, bool loadImplementation = true); /// <summary> /// Получить метаданные по уникальному идентификатору /// </summary> /// <param name="uid">Уникальный идентификатор метаданных</param> /// <param name="loadImplementation">Загружать ли метаданные реализации, если указанный тип - интерфейс</param> /// <returns>Метаданные</returns> IMetadata GetMetadataByUid(Guid uid, bool loadImplementation = true); /// <summary> /// Зарегистрировать тип /// </summary> /// <param name="type">Тип</param> void RegisterType(Type type); /// <summary> /// Получить список метаданных /// </summary> /// <returns>Список метаданных</returns> IEnumerable<IMetadata> GetMetadataList(); /// <summary> /// Получить описание типа данных по его уникальному идентификатору. Если не нашли - null /// </summary> /// <param name="typeUid">Уникальный идентификатор типа данных</param> /// <returns>Описание типа данных</returns> ITypeDescriptor GetTypeDescriptor(Guid typeUid); /// <summary> /// Получить описание типа данных по его типу CLR. Если не нашли - null /// </summary> /// <param name="runtimeType">Тип CLR</param> /// <returns>Описание типа данных</returns> ITypeDescriptor GetTypeDescriptor(Type runtimeType); /// <summary> /// Получить описание типа данных, который можно использовать для первичного ключа, по его уникальному идентфикатору. Если не нашли - null /// </summary> /// <param name="typeUid">Уникальный идентификатор типа данных</param> /// <returns>Описание типа данных</returns> ITypeDescriptor GetIdTypeDescriptor(Guid typeUid); } }